1
0
Fork 0

upload image

master
panliang 2023-09-23 14:00:38 +08:00
parent 360825af63
commit 7cdea4108f
21 changed files with 291 additions and 1813 deletions

View File

@ -1,3 +1,3 @@
ENV = 'production'
VUE_APP_BASE_API = 'http://baozhitang.abcdefg.fun'
VUE_APP_BASE_API = 'http://www.xbzt.cc'

View File

@ -4,6 +4,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
@ -21,5 +22,4 @@
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -1,85 +0,0 @@
# 富文本编辑器插件
uniapp 富文本编辑器插件
## 兼容性
|微信小程序|H5|APP|
|:--:|:--:|:--:|
|√|√ |x|
## 使用方式
`script` 中引用组件
```js
import myeditor from "@/components/robin-editor/editor.vue"
export default {
components: {myeditor}
}
```
`template` 中使用组件
```html
<myeditor class="editor"
@cancel="hideEditor"
@save="saveEditor"
v-model="html"
:imageUploader="uploadImg"
:muiltImage="true">
</myeditor>
```
## Demo
https://github.com/health901/uniapp-editor-demo
## 属性说明
|属性|类型|默认值|说明|
|--|--|--|--|
|v-model|String| |富文本,双向绑定|
|imageUploader|function(img,callback)| |上传图片处理函数 接受参数 img:本地图片地址,callback:上传成功回调传入图片链接|
|muiltImage|Boolean|false|是否支持多图上传|
|compressImage|Boolean|true|图片上传是否压缩|
|previewMode|Boolean|false|预览模式,不可编辑|
|autoHideToolbar|Boolean|false|失去焦点时自动隐藏工具栏|
|tools|Array|['bold', 'italic', 'underline', 'strike', 'align-left', 'align-center', 'align-right', 'remove', 'font', 'color', 'backgroundColor','image', 'clear', 'preview']|工具栏|
### 工具栏
|名称|值|
|--|--|
|加粗|`bold`|
|斜体|`italic`|
|下划线|`underline`|
|删除线|`strike`|
|右对齐|`align-left`|
|居中|`align-center`|
|左对齐|`align-right`|
|清除格式|`remove`|
|字体大小|`font`|
|字体颜色|`color`|
|背景色|`backgroundColor`|
|插入图片|`image`|
|清空|`clear`|
|预览|`preview`|
|插入日期|`date`|
|列表|`list-check`,`list-ordered`,`list-bullet`|
|上下标|`sub`,`super`|
|撤销,恢复撤销|`undo`,`redo`|
|缩进|`indent`,`outdent`|
|分割线|`divider`|
|标题|`h1`,`h2`,`h3`,`h4`,`h5`,`h6`|
|书写方向|`rtl`|
## 事件说明
|事件|说明|参数|
|--|--|--|
|cancel|点击取消按钮|
|save|点击保存按钮|e={html,text,delta}|
## 依赖
|组件|链接|备注|
|---|--|--|
|Popup 弹出层<sup>[[1]](#注)</sup>|https://ext.dcloud.net.cn/plugin?id=329|uni-ui库|
|Transition动画|https://ext.dcloud.net.cn/plugin?id=1231|uni-ui库,Popup依赖|
|颜色选择器ColorPicker<sup>[[2]](#注)</sup>|https://ext.dcloud.net.cn/plugin?id=1237|字体颜色,背景色|
## 注
1. 修改:新增动画结束事件
2. 修改:添加按钮,支持预设颜色值

View File

@ -1,289 +0,0 @@
<template>
<view class="content">
<topbar class="head" @cancel="cancel" @save="confirm"></topbar>
<view class="color-picker">
<view class="color-name">{{ colorName }}</view>
<view class="show-view" :style="{ background: colorName }"></view>
<view class="hue-view" @touchstart="pickHue" @touchmove="pickHue">
<text class="anchor" :style="{ left: hueView.anchorLeft + 'px' }"></text>
</view>
<view class="color-view" @touchstart="pickColor" @touchmove="pickColor" :style="{ backgroundColor: 'hsl(' + hueView.H + ', 100%, 50%)' }">
<text class="anchor" :style="{ top: colorView.anchorTop + 'px', left: colorView.anchorLeft + 'px' }"></text>
</view>
</view>
</view>
</template>
<script>
import topbar from '@/components/robin-editor/header.vue'
export default {
components: {
topbar
},
props: {
color: {
type: String,
default: ""
},
show: {
type: Boolean,
default: false
}
},
data() {
return {
hueView: {},
colorView: {},
colorName: '',
hueLeft: 0.5, // [0, 1]
anchorTop: 0.5, // top [0, 1]
anchorLeft: 0.5, // left [0, 1]
};
},
watch: {
show: function(newval, oldvar) {
if (!oldvar && newval) {
this.init()
}
}
},
mounted() {
if (this.show) {
this.init()
}
},
methods: {
init() {
const reg = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})*$/
if (this.color !== '' && reg.test(this.color)) {
this.getColorOffset()
}
Promise.all([this.getHueViewOffset(), this.getColorViewOffset()]).then(() => {
this.colorName = this.getColorString() // HLS RGB
})
},
getHueViewOffset() { //
return new Promise(resolve => uni.createSelectorQuery().in(this).select('.hue-view').boundingClientRect(
data => {
this.hueView = {
...data,
anchorLeft: data.width * this.hueLeft,
H: this.hueLeft * 360,
}
resolve()
}).exec())
},
getColorViewOffset() { //
return new Promise(resolve => uni.createSelectorQuery().in(this).select('.color-view').boundingClientRect(
data => {
this.colorView = {
...data,
anchorTop: data.height * this.anchorTop,
anchorLeft: data.width * this.anchorLeft,
S: this.anchorLeft,
L: (1 - this.anchorLeft * 0.5) - this.anchorTop / (this.anchorLeft + 1)
}
resolve()
}).exec())
},
getColorString() { // RGB
const arr = hslToRgb(this.hueView.anchorLeft / this.hueView.width, this.colorView.S, this.colorView.L)
const r = arr[0].toString(16).length === 1 ? `0${arr[0].toString(16)}` : arr[0].toString(16)
const g = arr[1].toString(16).length === 1 ? `0${arr[1].toString(16)}` : arr[1].toString(16)
const b = arr[2].toString(16).length === 1 ? `0${arr[2].toString(16)}` : arr[2].toString(16)
return `#${r.toUpperCase()}${g.toUpperCase()}${b.toUpperCase()}`
},
getColorOffset() {
var color = this.color.substr(1)
color = color.length == 6 ? color : color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(
1) + color.charAt(2) + color.charAt(2)
const r = parseInt("0x" + color.substr(0, 2))
const g = parseInt("0x" + color.substr(2, 2))
const b = parseInt("0x" + color.substr(4, 2))
const arr = rgbToHsl(r, g, b)
this.hueLeft = arr[0]
this.anchorLeft = arr[1]
this.anchorTop = ((1 - arr[1] * 0.5) - arr[2]) * (arr[1] + 1)
},
pickColor(e) { //
const top = e.touches[0].clientY - this.colorView.top
const left = e.touches[0].clientX - this.colorView.left
if (top < 0) {
this.colorView.anchorTop = 0
} else if (top > this.colorView.height) {
this.colorView.anchorTop = this.colorView.height
} else {
this.colorView.anchorTop = top
}
if (left < 0) {
this.colorView.anchorLeft = 0
} else if (left > this.colorView.width) {
this.colorView.anchorLeft = this.colorView.width
} else {
this.colorView.anchorLeft = e.touches[0].clientX - this.colorView.left
}
this.colorView.S = this.colorView.anchorLeft / this.colorView.width
this.colorView.L = this.floor((1 - this.colorView.S * 0.5) - this.colorView.anchorTop / this.colorView.height /
(this.colorView.S + 1))
this.colorName = this.getColorString() // HLS RGB
},
pickHue(e) { //
if (e.touches[0].clientX >= this.hueView.left && e.touches[0].clientX <= this.hueView.right) {
this.hueView.anchorLeft = e.touches[0].clientX - this.hueView.left
this.hueView.H = this.hueView.anchorLeft / this.hueView.width * 360
this.colorName = this.getColorString() // HLS RGB
}
},
floor(num) {
return num < 0.09 ? 0 : num
},
confirm() {
this.$emit('confirm', {
color: this.colorName
})
},
cancel(){
this.$emit('cancel')
}
}
}
function hslToRgb(h, s, l) { // HSL RGB
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
var round = function(n, l) {
return Math.round(n * Math.pow(10, l)) / Math.pow(10, l)
}
return [round(h, 3), round(s, 3), round(l, 3)];
}
</script>
<style lang="scss" scoped>
.content {
height: 100%;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
background-color: #fff;
.head {
width: 100%;
}
.color-picker {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
.color-name {
margin: 23rpx;
font-size: 45rpx;
font-weight: bold;
letter-spacing: 8rpx;
}
.show-view {
height: 56rpx;
width: 567rpx;
}
.hue-view {
width: 567rpx;
height: 56rpx;
margin: 12rpx 0;
position: relative;
background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
.anchor {
width: 12rpx;
height: 100%;
position: absolute;
background: #FFFFFF;
transform: translate(-50%);
box-shadow: 0 0 2rpx rgba(0, 0, 0, 0.6);
}
}
.color-view {
width: 567rpx;
height: 345rpx;
position: relative;
margin-bottom: 12upx;
&::before,
&::after {
content: '';
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
&::before {
background: linear-gradient(to right, white, transparent);
}
&::after {
background: linear-gradient(to top, black, transparent);
}
.anchor {
z-index: 1;
width: 24rpx;
height: 24rpx;
border-radius: 50%;
position: absolute;
border: 4rpx solid #FFFFFF;
background: rgba(0, 0, 0, .3);
transform: translate(-50%, -50%);
}
}
}
}
</style>

View File

@ -1,33 +1,31 @@
<template>
<u-popup :show="show">
<robin-editor
v-model="value"
class="editor"
@cancel="close"
@save="confirm"
:imageUploader="uploadImage"
/>
</u-popup>
<u-modal :show="show" showCancelButton @cancel="close" @confirm="confirm">
<editor id="editor" placeholder="请输入内容" @ready="onEditorReady" />
</u-modal>
</template>
<script>
import RobinEditor from '@/components/robin-editor/editor'
export default {
components: { RobinEditor },
name: 'CuEditor',
props: {
placeholder: {
type: String,
default: ''
}
},
data() {
return {
value: '',
show: false,
editor: ''
}
},
methods: {
onEditorReady() {
uni.createSelectorQuery().select('#editor').context((res) => {
this.editor = res.context
if (this.value) {
this.editor.setContents({
html: this.value
})
}
}).exec()
},
open(value) {
this.value = value
this.show = true
@ -35,22 +33,16 @@ export default {
close() {
this.show = false
},
confirm(e) {
confirm() {
if (this.editor) {
this.editor.getContents({
success: (e) => {
this.$emit('confirm', e)
this.close()
},
uploadImage(file, callback) {
this.$ajax.upload('/admin-api/upload_image', {
params: {'full-url': 1},
name: 'file',
filePath: file.path,
custom: { toast: false }
}).then(res => {
if (res.status == 0) {
callback(res.data.value)
}
})
}
},
}
}
</script>

View File

@ -0,0 +1,82 @@
<template>
<u-upload
ref="cu-upload"
:fileList="fileList"
:maxCount="count"
:multiple="multiple"
previewFullImage
@afterRead="afterRead"
@delete="deletePic"
/>
</template>
<script>
export default {
name: 'CuImage',
props: {
count: {
type: Number,
default: 5
},
multiple: {
type: Boolean,
default: false
}
},
data() {
return {
fileList: []
}
},
methods: {
deletePic(event) {
uni.showModal({
title: '删除图片',
content: '是否确定?',
success: (res) => {
if (res.confirm) {
this.fileList.splice(event.index, 1)
this.$emit('update', this.fileList)
}
}
})
},
async afterRead(event) {
let lists = [].concat(event.file)
let fileListLen = this.fileList.length
lists.map((item) => {
this.fileList.push({
...item,
status: 'uploading',
message: '上传中'
})
})
for (let i = 0; i < lists.length; i++) {
const res = await this.upload(lists[i])
let item = this.fileList[fileListLen]
this.fileList.splice(fileListLen, 1, Object.assign(item, {
status: res.status == 0 ? 'success' : 'failed',
message: res.msg,
url: res.status == 0 ? res.data.value : lists[i].url
}))
fileListLen++
}
this.$emit('update', this.fileList)
},
upload(file) {
return this.$ajax.upload('/admin-api/upload_image', {
params: { 'full-url': 1 },
filePath: file.url,
custom: { toast: false, loading: false },
name: 'file'
})
},
getList() {
return this.fileList
},
setList(list) {
this.fileList = list
},
}
}
</script>

View File

@ -1,236 +0,0 @@
@font-face {
font-family: "iconfont";
src: url('~./editor-icon.ttf') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-redo:before {
content: "\e627";
}
.icon-undo:before {
content: "\e633";
}
.icon-indent:before {
content: "\eb28";
}
.icon-outdent:before {
content: "\e6e8";
}
.icon-fontsize:before {
content: "\e6fd";
}
.icon-format-header-1:before {
content: "\e860";
}
.icon-format-header-4:before {
content: "\e863";
}
.icon-format-header-5:before {
content: "\e864";
}
.icon-format-header-6:before {
content: "\e865";
}
.icon-clearup:before {
content: "\e64d";
}
.icon-preview:before {
content: "\e631";
}
.icon-date:before {
content: "\e63e";
}
.icon-fontbgcolor:before {
content: "\e678";
}
.icon-clearedformat:before {
content: "\e67e";
}
.icon-font:before {
content: "\e684";
}
.icon-723bianjiqi_duanhouju:before {
content: "\e65f";
}
.icon-722bianjiqi_duanqianju:before {
content: "\e660";
}
.icon-text_color:before {
content: "\e72c";
}
.icon-format-header-2:before {
content: "\e75c";
}
.icon-format-header-3:before {
content: "\e75d";
}
.icon--checklist:before {
content: "\e664";
}
.icon-baocun:before {
content: "\ec09";
}
.icon-line-height:before {
content: "\e7f8";
}
.icon-quanping:before {
content: "\ec13";
}
.icon-direction-rtl:before {
content: "\e66e";
}
.icon-direction-ltr:before {
content: "\e66d";
}
.icon-selectall:before {
content: "\e62b";
}
.icon-fuzhi:before {
content: "\ec7a";
}
.icon-shanchu:before {
content: "\ec7b";
}
.icon-bianjisekuai:before {
content: "\ec7c";
}
.icon-fengexian:before {
content: "\ec7f";
}
.icon-dianzan:before {
content: "\ec80";
}
.icon-charulianjie:before {
content: "\ec81";
}
.icon-charutupian:before {
content: "\ec82";
}
.icon-wuxupailie:before {
content: "\ec83";
}
.icon-juzhongduiqi:before {
content: "\ec84";
}
.icon-yinyong:before {
content: "\ec85";
}
.icon-youxupailie:before {
content: "\ec86";
}
.icon-youduiqi:before {
content: "\ec87";
}
.icon-zitidaima:before {
content: "\ec88";
}
.icon-xiaolian:before {
content: "\ec89";
}
.icon-zitijiacu:before {
content: "\ec8a";
}
.icon-zitishanchuxian:before {
content: "\ec8b";
}
.icon-zitishangbiao:before {
content: "\ec8c";
}
.icon-zitibiaoti:before {
content: "\ec8d";
}
.icon-zitixiahuaxian:before {
content: "\ec8e";
}
.icon-zitixieti:before {
content: "\ec8f";
}
.icon-zitiyanse:before {
content: "\ec90";
}
.icon-zuoduiqi:before {
content: "\ec91";
}
.icon-zitiyulan:before {
content: "\ec92";
}
.icon-zitixiabiao:before {
content: "\ec93";
}
.icon-zuoyouduiqi:before {
content: "\ec94";
}
.icon-duigoux:before {
content: "\ec9e";
}
.icon-guanbi:before {
content: "\eca0";
}
.icon-shengyin_shiti:before {
content: "\eca5";
}
.icon-Character-Spacing:before {
content: "\e964";
}

View File

@ -1,488 +0,0 @@
<template>
<view class="wrapper">
<topbar class="header" @cancel="cancel" @save="save" :labelConfirm="labelConfirm" :labelCancel="labelCancel"></topbar>
<view :style="'height:' + editorHeight + 'px;'" class="container" v-if="!previewMode" v-show="!showPreview">
<editor
v-if="!previewMode"
v-show="!showPreview"
id="editor"
class="ql-container"
placeholder="开始输入..."
showImgSize
showImgToolbar
showImgResize
@statuschange="onStatusChange"
:read-only="readOnly"
@ready="onEditorReady"
></editor>
</view>
<view class="toolbar" @tap="format" v-if="!showPreview" v-show="keyboardHeight || !autoHideToolbar" :style="'bottom:' + (isIOS ? keyboardHeight : 0) + 'px'">
<block v-for="(t, i) in tools" :key="i">
<view v-if="t == 'bold'" :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold" data-label="加粗"></view>
<view v-if="t == 'italic'" :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic" data-label="斜体"></view>
<view v-if="t == 'underline'" :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline" data-label="下滑线"></view>
<view v-if="t == 'strike'" :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian" data-name="strike" data-label="删除线"></view>
<view
v-if="t == 'align-left'"
:class="formats.align === 'left' || !formats.align ? 'ql-active' : ''"
class="iconfont icon-zuoduiqi"
data-name="align"
data-value="left"
data-label="居左"
></view>
<view
v-if="t == 'align-center'"
:class="formats.align === 'center' ? 'ql-active' : ''"
class="iconfont icon-juzhongduiqi"
data-name="align"
data-value="center"
data-label="居中"
></view>
<view
v-if="t == 'align-right'"
:class="formats.align === 'right' ? 'ql-active' : ''"
class="iconfont icon-youduiqi"
data-name="align"
data-value="right"
data-label="居右"
></view>
<view
v-if="t == 'align-justify'"
:class="formats.align === 'justify' ? 'ql-active' : ''"
class="iconfont icon-zuoyouduiqi"
data-name="align"
data-value="justify"
data-label="平铺"
></view>
<!-- <view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-line-height" data-name="lineHeight"
data-value="2"></view>
<view :class="formats.letterSpacing ? 'ql-active' : ''" class="iconfont icon-Character-Spacing" data-name="letterSpacing"
data-value="2em"></view>
<view :class="formats.marginTop ? 'ql-active' : ''" class="iconfont icon-722bianjiqi_duanqianju" data-name="marginTop"
data-value="20px"></view>
<view :class="formats.previewarginBottom ? 'ql-active' : ''" class="iconfont icon-723bianjiqi_duanhouju"
data-name="marginBottom" data-value="20px"></view> -->
<view v-if="t == 'remove'" class="iconfont icon-clearedformat" @tap.stop="removeFormat"></view>
<picker v-if="t == 'font'" class="iconfont" mode="selector" :range="fontSizeRange" @change="fontSize"><view class="icon-fontsize"></view></picker>
<view
v-if="t == 'color'"
:style="fontColor != '#FFFFFF' ? 'color:' + formats.color : ''"
class="iconfont icon-text_color"
data-name="color"
@tap.stop="openColor"
></view>
<view
v-if="t == 'backgroundColor'"
:style="bgColor ? 'color:' + formats.backgroundColor : ''"
class="iconfont icon-fontbgcolor"
data-name="backgroundColor"
@tap.stop="openColor"
></view>
<view v-if="t == 'image'" class="iconfont icon-charutupian" @tap.stop="insertImage"></view>
<view v-if="t == 'clear'" class="iconfont icon-shanchu" @tap.stop="clear"></view>
<view v-if="t == 'preview'" class="iconfont icon-preview" @tap.stop="preview"></view>
<view v-if="t == 'date'" class="iconfont icon-date" @tap="insertDate"></view>
<view v-if="t == 'list-check'" class="iconfont icon-checklist" data-name="list" data-value="check"></view>
<view
v-if="t == 'list-ordered'"
:class="formats.list === 'ordered' ? 'ql-active' : ''"
class="iconfont icon-youxupailie"
data-name="list"
data-value="ordered"
></view>
<view v-if="t == 'list-bullet'" :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie" data-name="list" data-value="bullet"></view>
<view v-if="t == 'undo'" class="iconfont icon-undo" @tap="undo"></view>
<view v-if="t == 'redo'" class="iconfont icon-redo" @tap="redo"></view>
<view v-if="t == 'outdent'" class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
<view v-if="t == 'indent'" class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
<view v-if="t == 'divider'" class="iconfont icon-fengexian" @tap="insertDivider"></view>
<view v-if="t == 'h1'" :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1" data-name="header" :data-value="1"></view>
<view v-if="t == 'h2'" :class="formats.header === 2 ? 'ql-active' : ''" class="iconfont icon-format-header-2" data-name="header" :data-value="2"></view>
<view v-if="t == 'h3'" :class="formats.header === 3 ? 'ql-active' : ''" class="iconfont icon-format-header-3" data-name="header" :data-value="3"></view>
<view v-if="t == 'h4'" :class="formats.header === 4 ? 'ql-active' : ''" class="iconfont icon-format-header-4" data-name="header" :data-value="4"></view>
<view v-if="t == 'h5'" :class="formats.header === 5 ? 'ql-active' : ''" class="iconfont icon-format-header-5" data-name="header" :data-value="5"></view>
<view v-if="t == 'h6'" :class="formats.header === 6 ? 'ql-active' : ''" class="iconfont icon-format-header-6" data-name="header" :data-value="6"></view>
<view v-if="t == 'sub'" :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao" data-name="script" data-value="sub"></view>
<view v-if="t == 'super'" :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao" data-name="script" data-value="super"></view>
<view
v-if="t == 'rtl'"
:class="formats.direction === 'rtl' ? 'ql-active' : ''"
class="iconfont icon-direction-rtl"
data-name="direction"
:data-value="formats.direction === 'rtl' ? '' : 'rtl'"
></view>
</block>
</view>
<uni-popup ref="popup" type="bottom" @transed="colorPop">
<colorPicker :color="color" :show="showColor" @confirm="colorChanged" @cancel="colorPopClose"></colorPicker>
</uni-popup>
<view class="preview" v-show="showPreview"><rich-text :nodes="htmlData" class="previewNodes"></rich-text></view>
</view>
</template>
<script>
import colorPicker from '@/components/colorPicker.vue';
import uniPopup from '@/components/uni-popup/uni-popup.vue';
import topbar from './header.vue';
export default {
components: {
colorPicker,
uniPopup,
topbar
},
props: {
value: {
type: String
},
imageUploader: {
type: Function
},
muiltImage: {
type: Boolean,
default: false
},
compressImage: {
type: Boolean,
default: false
},
previewMode: {
type: Boolean,
default: false
},
autoHideToolbar: {
type: Boolean,
default: false
},
tools: {
type: Array,
default: function() {
return [
'bold',
'italic',
'underline',
'strike',
'align-left',
'align-center',
'align-right',
'remove',
'font',
'color',
'backgroundColor',
'image',
'clear',
'preview'
];
}
}
},
data() {
return {
show: true,
readOnly: false,
formats: {},
fontColor: '#000000',
bgColor: '',
color: '',
colorPickerName: '',
showColor: false,
fontSizeRange: [10, 12, 14, 16, 18, 24, 32],
showPreview: false,
htmlData: '',
html: '',
keyboardHeight: 0,
editorHeight: 0,
isIOS: false
};
},
watch: {
value: function(newvar) {
this.html = newvar;
},
html: function(newvar) {
if (this.previewMode) {
this.previewData(this.html);
}
if (this.editorCtx) {
this.editorCtx.setContents({
html: this.html
});
}
}
},
created() {
this.html = this.value;
},
mounted: function() {
const platform = uni.getSystemInfoSync().platform;
this.isIOS = platform === 'ios';
if (this.previewMode) {
this.previewData(this.html);
}
let keyboardHeight = 0;
this.updatePosition(0);
uni.onKeyboardHeightChange(res => {
if (res.height === keyboardHeight) return;
const duration = res.height > 0 ? res.duration * 1000 : 0;
keyboardHeight = res.height;
setTimeout(() => {
uni.pageScrollTo({
scrollTop: 0,
success: () => {
this.updatePosition(keyboardHeight);
this.editorCtx && this.editorCtx.scrollIntoView();
}
});
}, duration);
});
},
computed: {
labelConfirm: function() {
return this.showPreview ? '关闭' : '保存';
},
labelCancel: function() {
return this.showPreview ? '' : '取消';
}
},
methods: {
updatePosition(keyboardHeight) {
const { windowHeight, windowWidth, platform } = uni.getSystemInfoSync();
const rpx = windowWidth / 750;
let topbarHeight = 85 * rpx;
//#ifdef H5
topbarHeight += 44;
//#endif
const toolbarHeight = (70 * Math.ceil(this.tools.length / 15) + 1) * rpx;
const bodyHeight = windowHeight - topbarHeight;
this.keyboardHeight = keyboardHeight;
this.editorHeight = keyboardHeight > 0 ? bodyHeight - keyboardHeight - toolbarHeight : this.autoHideToolbar ? bodyHeight : bodyHeight - toolbarHeight;
},
openColor(e) {
var name = e.currentTarget.dataset.name;
var color = this.formats[name];
this.colorPickerName = name;
if (name == 'backgroundColor' && !color) {
color = '#FFFFFF';
}
if (name == 'color' && !color) {
color = '#000000';
}
this.color = color;
this.$refs.popup.open({
type: 'bottom'
});
},
colorPop(e) {
this.showColor = e.show;
},
colorPopClose() {
this.$refs.popup.close();
},
colorChanged(e) {
let label = '';
switch (this.colorPickerName) {
case 'backgroundColor':
if (e.color == '#FFFFFF') {
e.color = '';
}
this.bgColor = e.color;
label = '背景色';
break;
case 'color':
this.fontColor = e.color;
label = '颜色';
break;
}
this.colorPopClose();
this._format(this.colorPickerName, e.color, label + e.color);
},
readOnlyChange() {
this.readOnly = !this.readOnly;
},
onEditorReady() {
uni.createSelectorQuery()
.in(this)
.select('#editor')
.context(res => {
this.editorCtx = res.context;
if (this.html) {
this.editorCtx.setContents({
html: this.html
});
}
})
.exec();
},
undo() {
this.editorCtx.undo();
this.toast('撤销');
},
redo() {
this.editorCtx.redo();
this.toast('重做');
},
format(e) {
let { name, value, label } = e.target.dataset;
if (!name) return;
this._format(name, value, label);
},
_format(name, value, label) {
this.editorCtx.format(name, value);
this.toast(label);
},
toast(label) {
uni.showToast({
duration: 600,
icon: 'none',
title: label
});
},
onStatusChange(e) {
const formats = e.detail;
this.formats = formats;
},
insertDivider() {
this.editorCtx.insertDivider({
success: function() {
this.toast('插入分割线');
}
});
},
clear() {
this.editorCtx.clear({
success: res => {
this.toast('清空');
}
});
},
removeFormat() {
this.editorCtx.removeFormat();
this.toast('清除格式');
},
insertDate() {
const date = new Date();
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
this.editorCtx.insertText({
text: formatDate
});
this.toast('插入日期');
},
insertImage() {
let params = {};
params.count = this.muiltImage ? 9 : 1;
params.sizeType = this.compressImage ? ['compressed'] : ['original'];
uni.chooseImage({
...params,
success: res => {
res.tempFiles.map(path => {
this.imageUploader(path, url => {
this.editorCtx.insertImage({
src: url,
alt: '图像'
});
});
});
}
});
},
fontSize(e) {
const index = e.detail.value;
const fz = this.fontSizeRange[index] + 'px';
this._format('fontSize', fz, '字体大小:' + fz);
},
cancel() {
this.$emit('cancel');
},
save() {
if (this.showPreview) {
if (this.previewMode) {
this.cancel();
} else {
this.showPreview = false;
}
} else {
this.editorCtx.getContents({
success: res => {
this.$emit('save', res);
this.$emit('input', res.html);
}
});
}
},
previewData: function(html) {
this.htmlData = html.replace(/\<img/gi, '<img style="max-width:100%;height:auto"');
this.showPreview = true;
},
preview: function() {
this.editorCtx.getContents({
success: res => {
this.previewData(res.html);
}
});
}
}
};
</script>
<style lang="scss" scoped>
@import './editor-icon.css';
.wrapper {
padding: 5px;
width: 100%;
.header {
}
.container {
width: 100%;
margin-top: 20rpx;
background: #fff;
.ql-container {
box-sizing: border-box;
width: 100%;
height: 100%;
font-size: 16px;
line-height: 1.5;
overflow: auto;
padding: 20rpx;
}
}
.toolbar {
position: fixed;
width: 100%;
left: 0;
bottom: 0;
box-sizing: border-box;
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
background-color: #fff;
border-top: 1px solid #eee;
line-height: 50rpx;
.iconfont {
display: inline-block;
padding: 10rpx 0;
width: 50rpx;
text-align: center;
font-size: 34rpx;
box-sizing: border-box;
}
}
}
.preview {
width: 100%;
margin-top: 90rpx;
.previewNodes {
width: 100%;
word-break: break-all;
}
}
.ql-active {
color: #06c;
}
</style>

View File

@ -1,57 +0,0 @@
<template>
<view class="head">
<view class="btn left" @tap="cancel" v-if="labelCancel">{{labelCancel}}</view>
<view class="btn right" @tap="save" v-if="labelConfirm">{{labelConfirm}}</view>
</view>
</template>
<script>
export default {
props:{
labelCancel:{
type:String,
default:"取消"
},
labelConfirm:{
type:String,
default:"确定"
}
},
methods:{
cancel:function(){
this.$emit('cancel')
},
save:function(){
this.$emit('save')
}
}
}
</script>
<style lang="scss" scoped>
.head {
display: flex;
justify-content: space-between;
width: 100%;
height: 100%;
border-bottom: 1px #eee solid;
// box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
background: #fff;
.btn {
display: block;
width: 150upx;
height: 80upx;
line-height: 80upx;
font-size: 30upx;
color: #666;
padding-left: 20upx;
text-align: center;
&.left{
float: left;
}
&.right{
float: right;
}
}
}
</style>

View File

@ -38,7 +38,7 @@ export default {
perPage: 20,
loading: true,
// 'loading', 'nomore', 'loadmore'
status: 'loading',
status: 'loadmore',
}
},
created() {
@ -58,6 +58,7 @@ export default {
if (refresh) {
this.page = 1
this.list = []
this.status = 'loadmore'
}
if (this.status == 'nomore') {
return ;

View File

@ -1,273 +0,0 @@
<template>
<view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
<uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
<view class="uni-popup__wrapper-box" @click.stop="clear">
<slot />
</view>
</uni-transition>
</view>
</template>
<script>
import uniTransition from '../uni-transition/uni-transition.vue'
/**
* PopUp 弹出层
* @description 弹出层组件为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @property {Boolean} animation = [ture|false] 是否开启动画
* @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
* @event {Function} change 打开关闭弹窗触发e={show: false}
*/
export default {
name: 'UniPopup',
components: {
uniTransition
},
props: {
//
animation: {
type: Boolean,
default: true
},
// top: bottomcenter
type: {
type: String,
default: 'center'
},
// maskClick
maskClick: {
type: Boolean,
default: true
}
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
maskClass: {
'position': 'fixed',
'bottom': 0,
'top': 0,
'left': 0,
'right': 0,
'backgroundColor': 'rgba(0, 0, 0, 0.4)'
},
transClass: {
'position': 'fixed',
'left': 0,
'right': 0,
}
}
},
watch: {
type: {
handler: function(newVal) {
switch (this.type) {
case 'top':
this.ani = ['slide-top']
this.transClass = {
'position': 'fixed',
'left': 0,
'right': 0,
}
break
case 'bottom':
this.ani = ['slide-bottom']
this.transClass = {
'position': 'fixed',
'left': 0,
'right': 0,
'bottom': 0
}
break
case 'center':
this.ani = ['zoom-out', 'fade']
this.transClass = {
'position': 'fixed',
/* #ifndef APP-NVUE */
'display': 'flex',
'flexDirection': 'column',
/* #endif */
'bottom': 0,
'left': 0,
'right': 0,
'top': 0,
'justifyContent': 'center',
'alignItems': 'center'
}
break
}
},
immediate: true
}
},
created() {
if (this.animation) {
this.duration = 300
} else {
this.duration = 0
}
},
methods: {
clear(e) {
// TODO nvue
e.stopPropagation()
},
open() {
this.showPopup = true
this.$nextTick(() => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.showTrans = true
}, 50);
setTimeout(()=>{
this.$emit('transed', {
show: true
})
},this.duration)
})
this.$emit('change', {
show: true
})
},
close(type) {
this.showTrans = false
this.$nextTick(() => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.$emit('change', {
show: false
})
this.showPopup = false
setTimeout(()=>{
this.$emit('transed', {
show: false
})
},this.duration)
}, 300)
})
},
onTap() {
if (!this.maskClick) return
this.close()
}
}
}
</script>
<style lang="scss" scoped>
.uni-popup {
position: fixed;
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
bottom: 0;
left: 0;
right: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-popup__mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
opacity: 0;
}
.mask-ani {
transition-property: opacity;
transition-duration: 0.2s;
}
.uni-top-mask {
opacity: 1;
}
.uni-bottom-mask {
opacity: 1;
}
.uni-center-mask {
opacity: 1;
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: absolute;
}
.top {
top: 0;
left: 0;
right: 0;
transform: translateY(-500px);
}
.bottom {
bottom: 0;
left: 0;
right: 0;
transform: translateY(500px);
}
.center {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
bottom: 0;
left: 0;
right: 0;
top: 0;
justify-content: center;
align-items: center;
transform: scale(1.2);
opacity: 0;
}
.uni-popup__wrapper-box {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
}
.content-ani {
// transition: transform 0.3s;
transition-property: transform, opacity;
transition-duration: 0.2s;
}
.uni-top-content {
transform: translateY(0);
}
.uni-bottom-content {
transform: translateY(0);
}
.uni-center-content {
transform: scale(1);
opacity: 1;
}
</style>

View File

@ -1,279 +0,0 @@
<template>
<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
@click="change">
<slot></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: Array,
default () {
return []
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default () {
return {}
}
}
},
data() {
return {
isShow: false,
transform: '',
ani: { in: '',
active: ''
}
};
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
this.close()
}
},
immediate: true
}
},
computed: {
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transfrom = ''
for (let i in styles) {
let line = this.toLine(i)
transfrom += line + ':' + styles[i] + ';'
}
return transfrom
}
},
created() {
// this.timer = null
// this.nextTick = (time = 50) => new Promise(resolve => {
// clearTimeout(this.timer)
// this.timer = setTimeout(resolve, time)
// return this.timer
// });
},
methods: {
change() {
this.$emit('click', {
detail: this.isShow
})
},
open() {
clearTimeout(this.timer)
this.isShow = true
this.transform = ''
this.ani.in = ''
for (let i in this.getTranfrom(false)) {
if (i === 'opacity') {
this.ani.in = 'fade-in'
} else {
this.transform += `${this.getTranfrom(false)[i]} `
}
}
this.$nextTick(() => {
setTimeout(() => {
this._animation(true)
}, 50)
})
},
close(type) {
clearTimeout(this.timer)
this._animation(false)
},
_animation(type) {
let styles = this.getTranfrom(type)
// #ifdef APP-NVUE
if(!this.$refs['ani']) return
animation.transition(this.$refs['ani'].ref, {
styles,
duration: this.duration, //ms
timingFunction: 'ease',
needLayout: false,
delay: 0 //ms
}, () => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
})
// #endif
// #ifndef APP-NVUE
this.transform = ''
for (let i in styles) {
if (i === 'opacity') {
this.ani.in = `fade-${type?'out':'in'}`
} else {
this.transform += `${styles[i]} `
}
}
this.timer = setTimeout(() => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
}, this.duration)
// #endif
},
getTranfrom(type) {
let styles = {
transform: ''
}
this.modeClass.forEach((mode) => {
switch (mode) {
case 'fade':
styles.opacity = type ? 1 : 0
break;
case 'slide-top':
styles.transform += `translateY(${type?'0':'-100%'}) `
break;
case 'slide-right':
styles.transform += `translateX(${type?'0':'100%'}) `
break;
case 'slide-bottom':
styles.transform += `translateY(${type?'0':'100%'}) `
break;
case 'slide-left':
styles.transform += `translateX(${type?'0':'-100%'}) `
break;
case 'zoom-in':
styles.transform += `scale(${type?1:0.8}) `
break;
case 'zoom-out':
styles.transform += `scale(${type?1:1.2}) `
break;
}
})
return styles
},
_modeClassArr(type) {
let mode = this.modeClass
if (typeof(mode) !== "string") {
let modestr = ''
mode.forEach((item) => {
modestr += (item + '-' + type + ',')
})
return modestr.substr(0, modestr.length - 1)
} else {
return mode + '-' + type
}
},
// getEl(el) {
// console.log(el || el.ref || null);
// return el || el.ref || null
// },
toLine(name) {
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
}
}
}
</script>
<style>
.uni-transition {
transition-timing-function: ease;
transition-duration: 0.3s;
transition-property: transform, opacity;
}
.fade-in {
opacity: 0;
}
.fade-active {
opacity: 1;
}
.slide-top-in {
/* transition-property: transform, opacity; */
transform: translateY(-100%);
}
.slide-top-active {
transform: translateY(0);
/* opacity: 1; */
}
.slide-right-in {
transform: translateX(100%);
}
.slide-right-active {
transform: translateX(0);
}
.slide-bottom-in {
transform: translateY(100%);
}
.slide-bottom-active {
transform: translateY(0);
}
.slide-left-in {
transform: translateX(-100%);
}
.slide-left-active {
transform: translateX(0);
opacity: 1;
}
.zoom-in-in {
transform: scale(0.8);
}
.zoom-out-active {
transform: scale(1);
}
.zoom-out-in {
transform: scale(1.2);
}
</style>

View File

@ -64,6 +64,9 @@
"navigationBarTitleText": "宝芝堂",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
// #ifdef H5
,"navigationStyle": "custom"
// #endif
},
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"

View File

@ -1,11 +1,21 @@
<template>
<view class="page">
<u-grid align="center" :border="true" :col="2">
<view class="btns">
<view class="btn" v-for="item in list" :key="item.id">
<u-button
:icon="`../../static/${item.key}.png`"
:text="item.name"
size="large"
@click="itemClick(item.id)"
/>
</view>
</view>
<!-- <u-grid align="center" :border="true" :col="1">
<u-grid-item v-for="item in list" :key="item.id" :name="item.id" @click="itemClick">
<u-image :src="`../../static/${item.key}.png`" width="40" height="40" />
<text class="grid-text">{{item.name}}</text>
</u-grid-item>
</u-grid>
</u-grid> -->
</view>
</template>
@ -35,9 +45,25 @@
}
</script>
<style scoped>
.u-grid {
margin-top: 200px;
.page ::v-deep .u-grid {
margin-top: 400rpx;
background: white;
padding: 10px 0;
}
.page ::v-deep .u-grid-item {
padding: 15px 0;
}
.page ::v-deep .grid-text {
margin-top: 10px
}
.btns {
margin-top: 400rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.btns .btn {
width: 90%;
margin-top: 20px;
}
</style>

View File

@ -55,13 +55,14 @@
title="病情描述"
isLink
rightIcon="edit-pen"
value="点击修改"
@click="openEditor"
/>
<u-cell
title="录入时间"
:value="info.created_at"
/>
<u-cell title="图片资料">
<view slot="value">
<cu-image ref="images" @update="updateImages" />
</view>
</u-cell>
<u-cell title="录入时间" :value="info.created_at"/>
</u-cell-group>
<div class="btn">
<u-button text="添加病历" type="primary" @click="addRecord" />
@ -99,7 +100,7 @@
@confirm="selectBirthday"
/>
<select-admin-user ref="select-admin-user" @select="selectDoctor" />
<cu-editor ref="editor" placeholder="请输入病情描述" @confirm="confirmEditor" />
<cu-editor ref="editor" @confirm="confirmEditor" />
</view>
</template>
@ -107,13 +108,18 @@
import gender from '../../enums/gender'
import SelectAdminUser from '../../components/select-admin-user'
import CuEditor from '../../components/cu-editor'
import CuImage from '../../components/cu-image'
export default {
components: {SelectAdminUser, CuEditor},
components: {SelectAdminUser, CuEditor, CuImage},
data() {
return {
id: '',
info: {},
info: {
images: [],
doctor_id: '',
doctor: {},
},
modal: {
type: 'text',
show: false,
@ -129,7 +135,7 @@ export default {
show: false,
name: '',
value: (new Date).getTime(),
minDate: (new Date('1900-1-1')).getTime()
minDate: (new Date('1900/1/1')).getTime()
},
}
},
@ -148,10 +154,22 @@ export default {
uni.showLoading()
const params = {_action: 'getData'}
this.$ajax.get(`/admin-api/patient/${this.id}`, { params }).then(res => {
uni.stopPullDownRefresh()
if (res.status == 0) {
this.info = res.data
this.info = {
...res.data,
birthday: res.data.birthday ? res.data.birthday.replaceAll('-', '/') : '',
treat_at: res.data.treat_at ? res.data.treat_at.replaceAll('-', '/') : '',
}
if (res.data.images) {
this.$refs['images'].setList(res.data.images.map(value => {
return { url: value }
}))
}
uni.setNavigationBarTitle({ title: this.info.name })
}
}).catch(error => {
uni.stopPullDownRefresh()
})
},
openModal(title, name) {
@ -199,7 +217,11 @@ export default {
toggleBirthday(name) {
if (name) {
this.datePicker.name = name
this.datePicker.value = this.info[name] ? Math.floor(this.info[name] * 1000) : (new Date).getTime()
const value = this.info[name]
if (value) {
}
this.datePicker.value = (new Date(value)).getTime()
}
this.datePicker.show = !this.datePicker.show
},
@ -230,6 +252,14 @@ export default {
})
this.closeDoctor()
},
updateImages(e) {
const list = e.map(item => item.url)
this.update({ images: list }).then(res => {
if (res.status == 0) {
console.log(res)
}
})
},
addRecord() {
return uni.navigateTo({ url: `/pages/record/form?patient=${this.id}` })
},
@ -270,6 +300,10 @@ export default {
</script>
<style scoped>
.page {
padding: 20px;
background: white;
}
.u-cell-group {
background: white;
}
@ -279,4 +313,8 @@ export default {
.btn .u-button {
margin-top: 20px;
}
.page ::v-deep .u-upload__wrap {
justify-content: flex-end;
}
.text-gray {color: gray}
</style>

View File

@ -40,10 +40,13 @@
<u-form-item label="病情描述" prop="illness" @click="openEditor">
<view class="input-text">
<!-- <rich-text v-if="form.illness" :nodes="form.illness" /> -->
<text :class="{'input-placeholder': !form.illness}">{{form.illness ? '点击修改' : '点击填写'}}</text>
<text :class="{'input-placeholder': !form.illness}">点击修改</text>
<u-icon name="arrow-right" />
</view>
</u-form-item>
<u-form-item label="图片资料" prop="images" borderBottom>
<cu-image ref="images" />
</u-form-item>
<view class="button">
<u-button text="提交" type="primary" @click="submit"></u-button>
</view>
@ -74,9 +77,10 @@
import gender from '../../enums/gender'
import SelectAdminUser from '../../components/select-admin-user'
import CuEditor from '../../components/cu-editor'
import CuImage from '../../components/cu-image'
export default {
components: {SelectAdminUser, CuEditor},
components: {SelectAdminUser, CuEditor, CuImage},
data() {
return {
id: '',
@ -88,7 +92,8 @@ export default {
address: '',
treat_at: '',
doctor_id: '',
illness: ''
illness: '',
images: []
},
rules: {
name: {
@ -189,6 +194,7 @@ export default {
this.form.illness = e.html
},
submit() {
this.form.images = this.$refs['images'].getList().map(item => item.url)
this.$refs['form'].validate().then(res => {
uni.showLoading()
if (this.id) {
@ -230,7 +236,7 @@ export default {
<style scoped>
.page {
padding: 0 20px;
padding: 20px;
background: white;
}
.input-text {

View File

@ -51,15 +51,19 @@
title="诊疗情况"
isLink
rightIcon="edit-pen"
value="点击修改"
@click="openEditor"
/>
<u-cell title="图片资料">
<view slot="value">
<cu-image ref="images" @update="updateImages" />
</view>
</u-cell>
<u-cell
title="下次就诊时间"
:value="info.next_treat_at | date"
isLink
rightIcon="edit-pen"
@click="toggleDatePicker('next_treat_at')"
@click="toggleDatePicker('next_treat_at', 'datetime')"
/>
<u-cell
title="通知人"
@ -70,10 +74,10 @@
/>
<u-cell
title="通知时间"
:value="info.notify_at | date"
:value="info.notify_at | date('yyyy-MM-dd')"
isLink
rightIcon="edit-pen"
@click="toggleDatePicker('notify_at')"
@click="toggleDatePicker('notify_at', 'date')"
/>
<u-cell
title="通知备注"
@ -101,7 +105,7 @@
/>
<u-datetime-picker
v-model="datePicker.value"
mode="datetime"
:mode="datePicker.mode"
:show="datePicker.show"
:closeOnClickOverlay="true"
:minDate="datePicker.minDate"
@ -131,7 +135,7 @@
@close="toggleOrderStatus"
@select="selectOrderStatus"
/>
<cu-editor ref="editor" placeholder="请输入诊疗情况" @confirm="confirmEditor" />
<cu-editor ref="editor" @confirm="confirmEditor" />
</view>
</template>
@ -139,9 +143,10 @@
import CuEditor from '../../components/cu-editor'
import SelectAdminUser from '../../components/select-admin-user'
import orderStatus from '../../enums/order_status'
import CuImage from '../../components/cu-image'
export default {
components: {SelectAdminUser, CuEditor},
components: {SelectAdminUser, CuEditor, CuImage},
data() {
return {
id: '',
@ -165,9 +170,10 @@ export default {
},
datePicker: {
show: false,
mode: 'datetime',
name: '',
value: (new Date).getTime(),
minDate: (new Date('1900-1-1')).getTime()
minDate: (new Date('1900/1/1')).getTime()
},
modal: {
type: 'text',
@ -207,7 +213,17 @@ export default {
this.$ajax.get(`/admin-api/record/${this.id}`, { params }).then(res => {
uni.stopPullDownRefresh()
if (res.status == 0) {
this.info = res.data
this.info = {
...res.data,
treat_at: res.data.treat_at ? res.data.treat_at.replaceAll('-', '/') : '',
next_treat_at: res.data.next_treat_at ? res.data.next_treat_at.replaceAll('-', '/') : '',
notify_at: res.data.notify_at ? res.data.notify_at.replaceAll('-', '/') : '',
}
if (res.data.images) {
this.$refs['images'].setList(res.data.images.map(value => {
return { url: value }
}))
}
this.type.text = this.info.type ? this.info.type.name : ''
this.adminUser.doctor_name = this.info.doctor ? this.info.doctor.name : ''
this.orderStatus.text = orderStatus.map[this.info.order_status]
@ -254,10 +270,16 @@ export default {
}
})
},
toggleDatePicker(name) {
toggleDatePicker(name, mode) {
if (name) {
this.datePicker.name = name
this.datePicker.value = this.info[name] ? Math.floor(this.info[name] * 1000) : (new Date).getTime()
const value = this.info[name]
if (value) {
this.datePicker.value = (new Date(value)).getTime()
}
}
if (mode) {
this.datePicker.mode = mode
}
this.datePicker.show = !this.datePicker.show
},
@ -317,6 +339,14 @@ export default {
}
})
},
updateImages(e) {
const list = e.map(item => item.url)
this.update('images', list).then(res => {
if (res.status == 0) {
console.log(res)
}
})
},
deleteRecord() {
uni.showModal({
title: '删除病历记录',
@ -346,10 +376,17 @@ export default {
}
</script>
<style scoped>
.page {
padding: 20px;
background: white;
}
.btn {
padding: 0 10px;
}
.btn .u-button {
margin-top: 20px;
}
.text-gray {
color: gray;
}
</style>

View File

@ -55,11 +55,14 @@
<u-form-item label="请输入诊疗情况" prop="content" :borderBottom="true" @click="openEditor">
<view class="input-text">
<!-- <rich-text v-if="form.content" :nodes="form.content" /> -->
<text :class="{'input-placeholder': !form.content}">{{form.content ? '点击修改' : '点击填写'}}</text>
<text :class="{'input-placeholder': !form.content}">点击修改</text>
<u-icon name="arrow-right" />
</view>
</u-form-item>
<u-form-item prop="next_treat_at" label="下次就诊" :borderBottom="true" @click="toggleDatePicker('next_treat_at')">
<u-form-item label="图片资料" prop="images" borderBottom>
<cu-image ref="images" />
</u-form-item>
<u-form-item prop="next_treat_at" label="下次就诊" :borderBottom="true" @click="toggleDatePicker('next_treat_at', 'datetime')">
<view class="input-text">
<text v-if="form.next_treat_at">{{ form.next_treat_at | date }}</text>
<text v-else class="input-placeholder">请选择下次就诊时间</text>
@ -73,7 +76,7 @@
<u-icon name="arrow-right" />
</view>
</u-form-item>
<u-form-item prop="notify_at" label="通知时间" :borderBottom="true" @click="toggleDatePicker('notify_at')">
<u-form-item prop="notify_at" label="通知时间" :borderBottom="true" @click="toggleDatePicker('notify_at', 'date')">
<view class="input-text">
<text v-if="form.notify_at">{{ form.notify_at | date }}</text>
<text v-else class="input-placeholder">请选择通知时间</text>
@ -92,7 +95,7 @@
</u--form>
<u-datetime-picker
v-model="datePicker.value"
mode="datetime"
:mode="datePicker.mode"
:show="datePicker.show"
:closeOnClickOverlay="true"
:minDate="datePicker.minDate"
@ -101,7 +104,7 @@
@confirm="selectDatePicker"
/>
<select-admin-user ref="select-admin-user" @select="selectAdminUser" />
<cu-editor ref="editor" placeholder="请输入诊疗情况" @confirm="confirmEditor" />
<cu-editor ref="editor" @confirm="confirmEditor" />
</view>
</template>
@ -109,9 +112,10 @@
import CuEditor from '../../components/cu-editor'
import SelectAdminUser from '../../components/select-admin-user'
import orderStatus from '../../enums/order_status'
import CuImage from '../../components/cu-image'
export default {
components: {SelectAdminUser, CuEditor},
components: {SelectAdminUser, CuEditor, CuImage},
data() {
return {
id: '',
@ -155,6 +159,7 @@ export default {
typeList: [],
patient: {},
datePicker: {
mode: 'datetime',
show: false,
name: '',
value: (new Date).getTime(),
@ -243,8 +248,8 @@ export default {
},
methods: {
submit() {
this.form.images = this.$refs['images'].getList().map(item => item.url)
this.$refs['form'].validate().then(res => {
console.log(res)
uni.showLoading()
if (this.id) {
this.update()
@ -280,11 +285,14 @@ export default {
}
})
},
toggleDatePicker(name) {
toggleDatePicker(name, mode) {
if (name) {
this.datePicker.name = name
this.datePicker.value = this.form[name] ? Math.floor(this.form[name] * 1000) : (new Date).getTime()
}
if (mode) {
this.datePicker.mode = mode
}
this.datePicker.show = !this.datePicker.show
},
selectDatePicker(e) {
@ -323,12 +331,8 @@ export default {
</script>
<style scoped>
.page {
padding: 0 20px;
padding: 20px;
background: white;
position: absolute;
height: 100%;
width: 100%;
box-sizing: border-box;
}
.input-text {
color: #303133;

View File

@ -8,15 +8,9 @@
<u-swipe-action>
<u-list-item v-for="item in list" :key="item.id">
<u-swipe-action-item :options="swipeOption" :name="item.id" @click="swipeClick">
<u-cell
:title="item.treat_at"
:clickable="true"
:isLink="false"
size="large"
:center="true"
:url="`/pages/record/detail?id=${item.id}`"
>
<view slot="value">
<u-cell size="large" :url="`/pages/record/detail?id=${item.id}`">
<view slot="title" class="title">
<view class="list-item-title">{{ item.doctor ? item.doctor.name : '' }}</view>
<view class="list-item-price">
<text style="color: #dd524d;font-size: 19px">
<u-icon name="rmb" color="#dd524d" size="19px" />
@ -24,9 +18,9 @@
</text>
<text style="color: #c0c0c0;text-decoration: line-through;">{{ item.origin_price }}</text>
</view>
<view v-if="item.doctor" class="list-item-doctor">
<text>{{ item.doctor.name }}</text>
</view>
<view slot="label" class="label">
<view>就诊时间: {{ item.treat_at }}</view>
</view>
</u-cell>
</u-swipe-action-item>
@ -180,21 +174,23 @@ export default {
</script>
<style scoped>
.u-list {
.page {
padding-top: 20px;
}
.list {
background: white;
}
.u-cell__label {
margin-top: 5px;
font-size: 12px;
color: #909193;
line-height: 18px;
.list .title {
display: flex;
flex-direction: row;
justify-content: space-between;
font-size: 18px;
}
.list-item-doctor {
text-align: right;
color: #007aff;
.list .label {
color: gray;
margin-top: 10px;
}
.u-icon {
.list-item-price ::v-deep .u-icon {
display: inline-block;
}
.add-button {

View File

@ -40,9 +40,9 @@ $uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16px;
$uni-font-size-sm: 14px;
$uni-font-size-base: 16px;
$uni-font-size-lg: 18px;
/* 图片尺寸 */
$uni-img-size-sm: 20px;