初始化仓库

This commit is contained in:
2024-09-19 12:57:55 +08:00
commit ddac24aa35
228 changed files with 34703 additions and 0 deletions

117
.gitignore vendored Normal file
View File

@@ -0,0 +1,117 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
package-lock.json

16
.hbuilderx/launch.json Normal file
View File

@@ -0,0 +1,16 @@
{ // launch.json 配置了启动调试时相关设置configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

144
App.vue Normal file
View File

@@ -0,0 +1,144 @@
<template>
<view></view>
</template>
<script>
import {
info,
personalData,
leadInfo,
servInfo,
} from "@/api/all.js"
export default {
onLaunch() {
// 获取小程序更新机制兼容
if (uni.canIUse('getUpdateManager')) {
const updateManager = uni.getUpdateManager()
// 检查是否有新版本发布
updateManager.onCheckForUpdate(function(res) {
if (res.hasUpdate) {
//小程序有新版本,则静默下载新版本,做好更新准备
updateManager.onUpdateReady(function() {
uni.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: function(res) {
if (res.confirm) {
//新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
} else if (res.cancel) {
//如果需要强制更新,则给出二次弹窗,如果不需要,则这里的代码都可以删掉了
uni.showModal({
title: '温馨提示',
content: '我们已经做了新的优化,请及时更新哦~',
showCancel: false, //隐藏取消按钮也可显示取消会走res.cancel然后从新开始提示
success: function(res) {
//第二次提示后,强制更新
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
uni.clearStorageSync()
} else if (res.cancel) {
//重新回到版本更新提示
autoUpdate()
}
}
})
}
}
})
})
// 新的版本下载失败
updateManager.onUpdateFailed(function() {
uni.showModal({
title: '温馨提示',
content: '新版本已经上线,请您删除当前小程序,重新搜索打开',
})
})
}
})
} else {
// 提示用户在最新版本的客户端上体验
uni.showModal({
title: '温馨提示',
content: '当前微信版本过低,可能无法使用该功能,请升级到最新版本后重试。'
})
}
},
created() {
if (uni.getStorageSync('token')) {
this.getInfo()
}
},
onShow: function() {},
onHide: function() {
// this.$store.dispatch('closeWebSocket')
},
methods: {
async getInfo() {
let role = uni.getStorageSync('identity')
let req = {};
const data = {
store_id: uni.getStorageSync('store_id') || '11001',
}
role == 1 && (req = await info(data))
role == 2 && (req = await personalData(data))
if (req.errcode == 0) {
console.log(req, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
// 未完善信息
if (req.data.user.status == 3) {
// this.$toast('您还没有完善信息,请先完善信息')
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
uni.navigateTo({
url: '/pages/workbench/examine'
})
return
}
if (role == 2 && req.data.user.status == 2) {
let logInfo = uni.getStorageSync('logInfo') || {}
let userInfo = {
...logInfo,
...(uni.getStorageSync('userInfo') || {}),
...req.data
}
userInfo.status = userInfo.user && userInfo.user.status != '' ? userInfo.user.status : userInfo
.status
uni.setStorageSync('identity', userInfo['role'])
uni.removeStorageSync('logInfo')
uni.setStorageSync('userInfo', userInfo)
setTimeout(() => {
console.log('mainnnnnnnnnnnnnnnnnnnnnnnn');
role == 2 && this.$go('/pages/pharmacist/index', 3)
}, 100)
}
if (role == 1 && req.data.DoctorInfo.user.status == 2) {
let logInfo = uni.getStorageSync('logInfo') || {}
let userInfo = {
...logInfo,
...(uni.getStorageSync('userInfo') || {}),
...req.data
}
userInfo.status = userInfo.user && userInfo.user.status != '' ? userInfo.user.status : userInfo
.status
uni.setStorageSync('identity', userInfo['role'])
uni.removeStorageSync('logInfo')
uni.setStorageSync('userInfo', userInfo)
setTimeout(() => {
console.log('mainnnnnnnnnnnnnnnnnnnnnnnn');
role == 1 && this.$go('/pages/workbench/index', 3)
}, 100)
}
} else {
this.$toast(req.msg)
}
}
}
}
</script>
<style lang="scss">
@import "uview-ui/index.scss";
@import "common/css/app.css";
</style>

1728
api/all.js Normal file

File diff suppressed because it is too large Load Diff

138
api/consult.js Normal file
View File

@@ -0,0 +1,138 @@
// 咨询相关接口
import {
req
} from '@/common/js/index.js';
/*接诊
order_id int 是 订单id
*/
export function imageOrderAccess(data) {
return req.request({
url: 'image-order/access',
method: 'post',
data
})
}
// 结束问诊 https://app.xiaokang88.com/service/v1/store-accept/register-over
export function accessEnd(data) {
return req.request({
url: 'store-accept/register-over',
method: 'post',
data
})
}
/*退诊
order_id int 是 订单id
reason_id int 是 退针原因id
*/
export function imageOrderRefuse(data) {
return req.request({
url: 'image-order/refuse',
method: 'post',
data
})
}
/*退诊原因
*/
export function RefuseReason(data) {
return req.request({
url: 'store-accept/refuse-list',
method: 'post',
data
})
}
/*患者详情
order_id int 是 订单id
*/
export function imageOrderPatientInfo(data) {
return req.request({
url: 'image-order/patient-info',
method: 'post',
data
})
}
/*患者详情
patient_id int 是 患者id
*/
export function storePatientInfo(data) {
return req.request({
url: 'store-accept/patient-detail',
method: 'post',
data
})
}
/*患者详情 接诊/拒诊
patient_id int 是 患者id status 1接诊2拒绝
*/
export function storePatientRefuse(data) {
return req.request({
url: 'store-accept/accept-or-refuse',
method: 'post',
data
})
}
/*咨询详情
order_id int 是 订单id
*/
export function imageOrderInfo(data) {
return req.request({
url: 'image-order/info',
method: 'post',
data
})
}
/*咨询列表 挂号列表
order_id int 是 订单id
*/
export function registerList(data) {
return req.request({
url: 'register/list',
method: 'post',
data
})
}
// 咨询列表角标
/**
* @param {Object} data
* 参数名称 必须 参数类型 说明
*/
export function listMain(data) {
return req.request({
url: 'image-order/list-main',
method: 'post',
data
})
}
/*服务端发送消息
ims_id int 是 会话id
session_type int 是 会话类型1医生发给用户3导医发给用户5医生和客服会话6药师和客服7导医和客服
content json 是 消息内容
*/
export function messageSend(data) {
return req.request({
url: 'im/message-send',
method: 'post',
data
})
}
/*会话消息记录
ims_id int 是 会话id
page int 否 1 页码
page_size int 否 20 每页条数
*/
export function messageList(data) {
return req.request({
url: 'im/message-list',
method: 'post',
data
})
}

164
common/common.js Normal file
View File

@@ -0,0 +1,164 @@
import $store from '@/store/index.js';
import {
registerList,
} from '@/api/consult.js'
class common {
constructor(arg) {
this.timer = null
this.url = arg.url
this.isOnline = false //是否在线
this.socket = null //socket对象
this.reconnectTime = 0
this.isOpenReconnect = true //是否重连
this.loadFinish = false;
// 获取当前用户相关信息
let user = uni.getStorageSync('userInfo')
this.user_token = uni.getStorageSync('token')
this.user = user ? user : {}
// 初始化聊天对象
this.TO = false;
// 连接和监听
if (this.user) {
this.connectSocket()
}
}
// 断线重连
reconnect() {
console.log(this.isOnline)
console.log("进入reconnect准备重新链接")
if (this.isOnline) {
return
}
if (this.reconnectTime >= 3) {
return this.reconnectConfirm()
}
this.reconnectTime += 1
console.log("重新链接")
// this.connectSocket();
}
// 连接socket
connectSocket() {
console.log("链接connectSocket")
this.socket = uni.connectSocket({
url: this.url,
header: {
authorization: `Bearer ${uni.getStorageSync('token')}`
},
method: 'POST',
complete: () => {}
})
// 监听连接成功
this.socket.onOpen && this.socket.onOpen(() => this.onOpen())
// 监听接收信息
this.socket.onMessage && this.socket.onMessage((res) => this.onMessage(res))
// 监听断开
this.socket.onClose && this.socket.onClose((e) => this.onClose(e))
// 监听错误
this.socket.onError && this.socket.onError(() => this.onError())
}
onOpen() {
// 用户上线
console.log('websocket连接成功')
this.isOnline = true
this.isOpenReconnect = true
console.log(this.url, 'doctor');
console.log(this.user, 'user')
// 获取用户离线消息
if (this.socket != null) {
var login = {
cmd: 'login',
userId: Number(this.user.su_id),
imStatus: Number(this.user.im_status),
userType: Number(this.user.role),
data: ''
};
this.initSocketLogin(login);
}
}
// 监听关闭
onClose(e) {
// 用户下线
this.isOnline = false
this.socket = null
console.log('socket连接已关闭' + e)
if (this.isOpenReconnect) {
var that = this;
setTimeout(function() {
that.reconnect();
}, 3000);
}
}
// 监听连接错误
onError() {
// 用户下线
this.isOnline = false
this.socket = null
console.log('socket连接错误' + e)
if (!this.socket) {
var that = this;
setTimeout(function() {
that.reconnect();
}, 3000);
}
}
// 监听接收消息
onMessage(data) {
console.log('监听接收消息:' + data);
// let res = JSON.parse(data.data)
// uni.$emit('onMessage', res)
}
// 关闭连接
close() {
if (this.socket) {
this.socket.close()
this.isOpenReconnect = false
} else if (uni.onSocketClose) {
uni.onSocketClose()
this.isOpenReconnect = false
}
this.destoryChatObject();
}
initSocketLogin(login) {
this.socket.send({
data: JSON.stringify(login),
success() {
console.log('用户IM登录成功:success')
},
fail(e) {
console.log('用户IM登录失败fail:' + e)
}
})
// setTimeout(() => {
// this.socket.send({
// data: JSON.stringify({
// data: '发送内容是:测试测试测试',
// }),
// })
// }, 4000)
}
// 创建聊天对象
createChatObject(detail) {
this.TO = detail
}
// 销毁聊天对象
destoryChatObject() {
this.TO = false
}
// 断线重连提示
reconnectConfirm() {
// this.close()
// this.connectSocket()
// this.reconnectTime = 0
}
// 验证是否上线
checkOnline() {
if (!this.isOnline) {
// 断线重连提示
this.reconnectConfirm()
return false
}
return true
}
}
export default common

482
common/css/app.css Normal file
View File

@@ -0,0 +1,482 @@
/* 安全区域距离顶部边界距离 */
.safe-area-inset-top {
padding-top: 0 !important;
padding-top: constant(safe-area-inset-top) !important;
padding-top: env(safe-area-inset-top) !important;
}
.safe-area-inset-bottom {
padding-bottom: 0 !important;
padding-bottom: constant(safe-area-inset-bottom) !important;
padding-bottom: env(safe-area-inset-bottom) !important;
}
.flex-con {
flex: 1;
}
.main-c {
color: #0D111A !important
}
.light-c {
color: #1E293B !important
}
.content-c {
color: #31353D !important
}
.tips-c {
color: #6C7380 !important
}
.error-c {
color: #F44336 !important
}
.primary-c {
color: #2979FF !important
}
.w-c {
color: white !important
}
.fs-2 {
font-size: 20rpx !important
}
.fs-24 {
font-size: 24rpx !important
}
.fs-28 {
font-size: 28rpx !important
}
.fs-3 {
font-size: 30rpx !important
}
.fs-32 {
font-size: 32rpx !important
}
.fs-36 {
font-size: 36rpx !important
}
.fs-4 {
font-size: 40rpx !important
}
.fs-44 {
font-size: 44rpx !important
}
.fs-48 {
font-size: 48rpx !important
}
.ff_pfsc {
font-family: 'PingFang SC' !important;
}
.fw-700 {
font-weight: 700;
}
.flex {
display: flex;
}
/* 竖直排列 */
.flex-col {
display: flex;
flex-direction: column;
}
/* 水平排列 */
.flex-row {
display: flex;
flex-direction: row;
}
.flex-nowrap {
flex-wrap: nowrap;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-1 {
flex: 1;
}
.flex-jus-center {
justify-content: center;
}
.flex-jus-sp {
justify-content: space-between;
}
.flex-jus-sa {
justify-content: space-around;
}
.flex-jus-se {
justify-content: space-evenly;
}
.flex-jus-start {
justify-content: flex-start;
}
.flex-jus-end {
justify-content: flex-end;
}
.flex-ali-center {
align-items: center;
}
.flex-ali-end {
align-items: flex-end;
}
.flex-ali-start {
align-items: flex-start;
}
.flex-ali-baseline {
align-items: baseline;
}
.grid-tem-col-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.grid-tem-col-3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.vain-btn {
margin: 0 !important;
padding: 0 !important;
border: none !important;
background-color: transparent !important;
}
.vain-btn::after {
border: none !important;
}
.m-l-1 {
margin-left: 10rpx;
}
.m-l-12 {
margin-left: 12rpx;
}
.m-r-16 {
margin-right: 16rpx;
}
.m-l-16 {
margin-left: 16rpx;
}
.m-l-2 {
margin-left: 20rpx;
}
.m-r-2 {
margin-right: 20rpx;
}
.m-r08 {
margin-right: 8rpx;
}
.m-l-24 {
margin-left: 24upx;
}
.m-l-32 {
margin-left: 32rpx;
}
.m-l-4 {
margin-left: 40rpx;
}
.m-l-48 {
margin-left: 48rpx;
}
.m-b-16 {
margin-bottom: 16rpx;
}
.m-b-2 {
margin-bottom: 20rpx;
}
.m-b-32 {
margin-bottom: 32rpx;
}
.m-b-48 {
margin-bottom: 48rpx;
}
.m-l-8 {
margin-left: 80rpx;
}
.m-t08 {
margin-top: 8rpx;
}
.p-col-08 {
padding: 8rpx 0;
}
.m-t-1 {
margin-top: 10rpx;
}
.m-t-12 {
margin-top: 12rpx;
}
.m-t-16 {
margin-top: 16rpx;
}
.m-t-2 {
margin-top: 20rpx;
}
.m-t-3 {
margin-top: 30rpx;
}
.m-t-32 {
margin-top: 30rpx;
}
.m-t-50 {
margin-top: 50rpx;
}
.m-t-4 {
margin-top: 40rpx;
}
.m-t-48 {
margin-top: 48rpx;
}
.m-t-8 {
margin-top: 80rpx;
}
.m-t-32 {
margin-top: 32rpx;
}
.m-t-24 {
margin-top: 24rpx;
}
.p-col-32 {
padding: 32rpx 0;
}
.p-col-12 {
padding: 12rpx 0;
}
.p-col-16 {
padding: 16rpx 0;
}
.p-col-2 {
padding: 20rpx 0;
}
.p-row-16 {
padding: 0 16rpx;
}
.p-16 {
padding: 16rpx;
}
.p-row-32 {
padding: 0 32rpx;
}
.p-row-48 {
padding: 0 48rpx;
}
.p-row-52 {
padding: 0 52rpx;
}
.p-t-32 {
padding-top: 32rpx;
}
.p-32 {
padding: 32rpx;
}
.p-t-0 {
padding-top: 0;
}
.p-2 {
padding: 20rpx;
}
.p-col-4 {
padding: 40rpx 0;
}
.white {
background-color: #fff;
}
.bg {
background-color: #F9FAFB;
}
.p-row-2 {
padding: 0 20rpx;
}
.full-width {
width: 100% !important;
}
.b-r-8 {
border-radius: 8rpx;
}
.b-r-16 {
border-radius: 16rpx;
}
.full-height {
height: 100% !important;
}
.seperator {
width: 1px;
background-color: #F7F6F6;
}
.seperator-h {
width: 100%;
height: 1px;
background-color: #F7F6F6;
}
.bottom-border {
border-bottom: 1px solid #F7F8FA;
}
.bottom-col-border {
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
.b-r-half {
border-radius: 50%;
}
/* 字体超出一行显示省略号 ,宽度自行设置,不支持多行*/
.text-hide {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.text-cut {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.text-twoline {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.btn-none {
padding: 0 !important;
border: none !important;
box-sizing: content-box;
}
.btn-none::after {
border: none !important;
}
.size-32 {
width: 32rpx;
height: 32rpx;
}
.text-align-justify {
text-align-last: justify !important;
-ms-text-align-last: justify !important;
-moz-text-align-last: justify !important
}
.text-align-start {
text-align-last: start !important;
-ms-text-align-last: start !important;
-moz-text-align-last: start !important
}
.text-align-end {
text-align-last: end !important;
-ms-text-align-last: end !important;
-moz-text-align-last: end !important;
}
.text-align-cen {
text-align: center !important;
}
.box-border {
box-sizing: border-box;
}
.box-content {
box-sizing: content-box;
}
.min-w-140 {
min-width: 140rpx;
}

917
common/getPingYin.js Normal file

File diff suppressed because one or more lines are too long

53
common/js/common.js Normal file
View File

@@ -0,0 +1,53 @@
export async function requestConfig(ins, options, successHandler = null, failHandler = null, completeHandler = null){
// base
ins.header = options.header || ins.header
//原来是ins.baseUrl = options.baseUrl || ins.baseUrl
let baseUrl = options.baseUrl || ins.baseUrl;
// config base
let config = {
url:`${baseUrl}${options.url?options.url:''}`,
header:ins.header
}
if(ins.requestInterceptor){
let _cg = null
// 合并请求拦截器配置
try {
_cg = await ins.requestInterceptor(Object.assign({}, options, config))
} catch (e){
return false
}
// config为false或null return
if(!_cg || typeof _cg !== 'object'){
return false
}
// 更新options
Object.assign(options, _cg)
config.url = options.url
config.header = options.header
}
const type = options.type || "request"
// config 详情 如果options没有直接使用删除props
if(type === "request"){
config["data"] = options.data || options.params || {}
config["method"] = options.method || "GET"
config["dataType"] = options.dataType || "json"
config["responseType"] = options.responseType || "text"
config['sslVerify'] = false
}else if(type === "upload"){
config["filePath"] = options.filePath
config["name"] = options.name
config["method"] = options.method || "POST"
config["formData"] = options.formData
// fileType for alipay
config["fileType"] = options.fileType || "image"
config['sslVerify'] = false
// 强制删除 Content-Type
delete config.header["Content-Type"]
}
return config
}
function _isPromise(obj) {
return obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
}

7
common/js/errorCode.js Normal file
View File

@@ -0,0 +1,7 @@
export default {
'500':'服务器错误',
'405': '资源被禁止',
'404': '操作失败',
'401': '无效的登录,或者登录已过期,请重新登录。',
'default': '未知错误'
}

384
common/js/handwriting.js Normal file
View File

@@ -0,0 +1,384 @@
export default class {
// 内置数据
ctx = '';
linePrack = []; //划线轨迹 ; 生成线条的实际点
currentLine = [];
transparent = 1; // 透明度
pressure = 0.5; // 默认压力
smoothness = 100; //顺滑度用60的距离来计算速度
lineSize = 1.5; // 笔记倍数
lineMin = 0.5; // 最小笔画半径
lineMax = 2; // 最大笔画半径
currentPoint = {};
firstTouch = true; // 第一次触发
radius = 1; //画圆的半径
cutArea = {
top: 0,
right: 0,
bottom: 0,
left: 0
}; //裁剪区域
lastPoint = 0;
chirography = []; //笔迹
startY = 0;
deltaY = 0;
startValue = 0;
constructor(opts, _this) {
this.lineColor = opts.lineColor || '#1A1A1A' // 颜色
this.lineSise = opts.lineSise || 50
this.canvasName = opts.canvasName || 'handWriting'
this.init(_this)
}
init(_this) {
this.ctx = uni.createCanvasContext(this.canvasName, _this)
}
// 笔迹开始
penStart(event) {
let e = event.mp
if (e.type != 'touchstart') return false;
this.ctx.setFillStyle(this.lineColor); // 初始线条设置颜色
this.ctx.setGlobalAlpha(this.transparent); // 设置半透明
this.currentPoint = {
x: e.touches[0].x,
y: e.touches[0].y
}
this.currentLine.unshift({
time: new Date().getTime(),
dis: 0,
x: this.currentPoint.x,
y: this.currentPoint.y
})
if (this.firstTouch) {
this.cutArea = {
top: this.currentPoint.y,
right: this.currentPoint.x,
bottom: this.currentPoint.y,
left: this.currentPoint.x
}
this.firstTouch = false
}
this.pointToLine(this.currentLine);
}
// 笔迹移动
penMove(event) {
let e = event.mp
if (e.type != 'touchmove') return false;
if (e.cancelable) {
// 判断默认行为是否已经被禁用
if (!e.defaultPrevented) {
e.preventDefault();
}
}
let point = {
x: e.touches[0].x,
y: e.touches[0].y
}
//测试裁剪
if (point.y < this.cutArea.top) {
this.cutArea.top = point.y;
}
if (point.y < 0) this.cutArea.top = 0;
if (point.x > this.cutArea.right) {
this.cutArea.right = point.x;
}
if (this.canvasWidth - point.x <= 0) {
this.cutArea.right = this.canvasWidth;
}
if (point.y > this.cutArea.bottom) {
this.cutArea.bottom = point.y;
}
if (this.canvasHeight - point.y <= 0) {
this.cutArea.bottom = this.canvasHeight;
}
if (point.x < this.cutArea.left) {
this.cutArea.left = point.x;
}
if (point.x < 0) this.cutArea.left = 0;
this.lastPoint = this.currentPoint;
this.currentPoint = point
this.currentLine.unshift({
time: new Date().getTime(),
dis: this.distance(this.currentPoint, this.lastPoint, 'move'),
x: point.x,
y: point.y
})
this.pointToLine(this.currentLine);
}
// 笔迹结束
penEnd(event) {
let e = event.mp
if (e.type != 'touchend') return 0;
let point = {
x: e.changedTouches[0].x,
y: e.changedTouches[0].y
}
this.lastPoint = this.currentPoint;
this.currentPoint = point
this.currentLine.unshift({
time: new Date().getTime(),
dis: this.distance(this.currentPoint, this.lastPoint, 'end'),
x: point.x,
y: point.y
})
if (this.currentLine.length > 2) {
var info = (this.currentLine[0].time - this.currentLine[this.currentLine.length - 1].time) / this
.currentLine.length;
//$("#info").text(info.toFixed(2));
}
//一笔结束,保存笔迹的坐标点,清空,当前笔迹
//增加判断是否在手写区域;
this.pointToLine(this.currentLine);
var currentChirography = {
lineSize: this.lineSize,
lineColor: this.lineColor
};
this.chirography.unshift(currentChirography);
this.linePrack.unshift(this.currentLine);
this.currentLine = []
}
retDraw() {
this.ctx.clearRect(0, 0, 700, 730)
this.ctx.draw()
}
//画两点之间的线条;参数为:line会绘制最近的开始的两个点
pointToLine(line) {
this.calcBethelLine(line);
// this.calcBethelLine1(line);
return;
}
//计算插值的方式;
calcBethelLine(line) {
if (line.length <= 1) {
line[0].r = this.radius;
return;
}
let x0, x1, x2, y0, y1, y2, r0, r1, r2, len, lastRadius, dis = 0,
time = 0,
curveValue = 0.5;
if (line.length <= 2) {
x0 = line[1].x
y0 = line[1].y
x2 = line[1].x + (line[0].x - line[1].x) * curveValue;
y2 = line[1].y + (line[0].y - line[1].y) * curveValue;
//x2 = line[1].x;
//y2 = line[1].y;
x1 = x0 + (x2 - x0) * curveValue;
y1 = y0 + (y2 - y0) * curveValue;;
} else {
x0 = line[2].x + (line[1].x - line[2].x) * curveValue;
y0 = line[2].y + (line[1].y - line[2].y) * curveValue;
x1 = line[1].x;
y1 = line[1].y;
x2 = x1 + (line[0].x - x1) * curveValue;
y2 = y1 + (line[0].y - y1) * curveValue;
}
//从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) (x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上
len = this.distance({
x: x2,
y: y2
}, {
x: x0,
y: y0
}, 'calc');
lastRadius = this.radius;
for (let n = 0; n < line.length - 1; n++) {
dis += line[n].dis;
time += line[n].time - line[n + 1].time;
if (dis > this.smoothness) break;
}
this.radius = Math.min(time / len * this.pressure + this.lineMin, this.lineMax) * this.lineSize
line[0].r = this.radius;
//计算笔迹半径;
if (line.length <= 2) {
r0 = (lastRadius + this.radius) / 2;
r1 = r0;
r2 = r1;
//return;
} else {
r0 = (line[2].r + line[1].r) / 2;
r1 = line[1].r;
r2 = (line[1].r + line[0].r) / 2;
}
let n = 5;
let point = [];
for (let i = 0; i < n; i++) {
let t = i / (n - 1);
let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;
let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;
let r = lastRadius + (this.radius - lastRadius) / n * i;
point.push({
x: x,
y: y,
r: r
});
if (point.length == 3) {
let a = this.ctaCalc(point[0].x, point[0].y, point[0].r, point[1].x, point[1].y, point[1].r, point[
2].x, point[2].y,
point[2].r);
a[0].color = this.lineColor;
this.bethelDraw(a, 1);
point = [{
x: x,
y: y,
r: r
}];
}
}
}
//求两点之间距离
distance(a, b, type) {
let x = b.x - a.x;
let y = b.y - a.y;
return Math.sqrt(x * x + y * y) * 5;
}
ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {
let a = [],
vx01, vy01, norm, n_x0, n_y0, vx21, vy21, n_x2, n_y2;
vx01 = x1 - x0;
vy01 = y1 - y0;
norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;
vx01 = vx01 / norm * r0;
vy01 = vy01 / norm * r0;
n_x0 = vy01;
n_y0 = -vx01;
vx21 = x1 - x2;
vy21 = y1 - y2;
norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;
vx21 = vx21 / norm * r2;
vy21 = vy21 / norm * r2;
n_x2 = -vy21;
n_y2 = vx21;
a.push({
mx: x0 + n_x0,
my: y0 + n_y0,
color: "#1A1A1A"
});
a.push({
c1x: x1 + n_x0,
c1y: y1 + n_y0,
c2x: x1 + n_x2,
c2y: y1 + n_y2,
ex: x2 + n_x2,
ey: y2 + n_y2
});
a.push({
c1x: x2 + n_x2 - vx21,
c1y: y2 + n_y2 - vy21,
c2x: x2 - n_x2 - vx21,
c2y: y2 - n_y2 - vy21,
ex: x2 - n_x2,
ey: y2 - n_y2
});
a.push({
c1x: x1 - n_x2,
c1y: y1 - n_y2,
c2x: x1 - n_x0,
c2y: y1 - n_y0,
ex: x0 - n_x0,
ey: y0 - n_y0
});
a.push({
c1x: x0 - n_x0 - vx01,
c1y: y0 - n_y0 - vy01,
c2x: x0 + n_x0 - vx01,
c2y: y0 + n_y0 - vy01,
ex: x0 + n_x0,
ey: y0 + n_y0
});
a[0].mx = a[0].mx.toFixed(1);
a[0].mx = parseFloat(a[0].mx);
a[0].my = a[0].my.toFixed(1);
a[0].my = parseFloat(a[0].my);
for (let i = 1; i < a.length; i++) {
a[i].c1x = a[i].c1x.toFixed(1);
a[i].c1x = parseFloat(a[i].c1x);
a[i].c1y = a[i].c1y.toFixed(1);
a[i].c1y = parseFloat(a[i].c1y);
a[i].c2x = a[i].c2x.toFixed(1);
a[i].c2x = parseFloat(a[i].c2x);
a[i].c2y = a[i].c2y.toFixed(1);
a[i].c2y = parseFloat(a[i].c2y);
a[i].ex = a[i].ex.toFixed(1);
a[i].ex = parseFloat(a[i].ex);
a[i].ey = a[i].ey.toFixed(1);
a[i].ey = parseFloat(a[i].ey);
}
return a;
}
bethelDraw(point, is_fill, color) {
this.ctx.beginPath();
this.ctx.moveTo(point[0].mx, point[0].my);
if (undefined != color) {
this.ctx.setFillStyle(color);
this.ctx.setStrokeStyle(color);
} else {
this.ctx.setFillStyle(point[0].color);
this.ctx.setStrokeStyle(point[0].color);
}
for (let i = 1; i < point.length; i++) {
this.ctx.bezierCurveTo(point[i].c1x, point[i].c1y, point[i].c2x, point[i].c2y, point[i].ex, point[i]
.ey);
}
this.ctx.stroke();
if (undefined != is_fill) {
this.ctx.fill(); //填充图形 ( 后绘制的图形会覆盖前面的图形, 绘制时注意先后顺序 )
}
this.ctx.draw(true)
}
selectColorEvent(lineColor) {
this.lineColor = lineColor;
}
selectSlideValue(lineSise) {
switch (lineSise) {
case 10:
this.lineSize = 0.1;
this.lineMin = 0.1;
this.lineMax = 0.1;
break;
case 20:
this.lineSize = 1;
this.lineMin = 0.5;
this.lineMax = 2;
break;
case 30:
this.lineSize = 1.5;
this.lineMin = 1;
this.lineMax = 3;
break;
case 40:
this.lineSize = 1.5;
this.lineMin = 2;
this.lineMax = 3.5;
break;
case 50:
this.lineSize = 3;
this.lineMin = 2;
this.lineMax = 3.5;
break;
}
}
saveCanvas(_this) {
return new Promise((resolve, rej) => {
uni.canvasToTempFilePath({
canvasId: this.canvasName,
success: (res) => {
uni.getFileSystemManager().readFile({
filePath: res.tempFilePath,
encoding: 'base64',
success: r => {
resolve(r.data);
}
})
},
fail: function(err) {
rej(err);
}
}, _this)
})
}
}

111
common/js/index.js Normal file
View File

@@ -0,0 +1,111 @@
import Request from './request.js';
import errorCode from './errorCode.js';
let protocol = 'https'; //协议
let baseUrl = 'app.xiaokang88.com/service/v1/'; // 域名
export const config = {
baseUrl: `${protocol}://${baseUrl}`
}
/**
* 请求拦截器
* @param {*} options
*/
const reqInterceptor = async (options) => {
options.header = {
...options.header
}
const isToken = (options.header || {}).isToken === false
if (uni.getStorageSync('token') && !isToken) {
options.header['authorization'] = `Bearer ${uni.getStorageSync('token')}`// 让每个请求携带自定义token 请根据实际情况自行修改
}
return options;
}
/**
* 响应拦截器
*/
const resInterceptor = (response, conf = {}) => {
// 网络状态码
const statusCode = response.statusCode|| response.errcode;
// 响应拦截器
if (statusCode >= 200 && statusCode < 300) {
_responseLog(response, conf, "response 200-299")
return response.data
} else if (statusCode === 500) {
uni.showToast({
icon: 'error',
title: response['msg'] || errorCode[statusCode]
})
_responseLog(response, conf, "response 500")
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息方便统一处理reject
// 以下内容作为reject的返回根据需求处理返回具体错误信息
msg: "服务器错误",
res: response
}
} else if (statusCode === 401) {
uni.showToast({
icon: 'error',
title: response['msg'] || errorCode[statusCode]
})
uni.removeStorageSync()
setTimeout(()=>{
uni.navigateTo({
url:'/pages/login/index'
})
},1000)
_responseLog(response, conf, "response 401")
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息方便统一处理reject
// 以下内容作为reject的返回根据需求处理返回具体错误信息
msg: response['msg'] || errorCode[statusCode],
res: response
}
} else {
console.log
uni.showToast({
icon: 'error',
title: response['msg'] || errorCode[statusCode]
})
_responseLog(response, conf, "response 300-499");
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息方便统一处理reject
// 以下内容作为reject的返回根据需求处理返回具体错误信息
msg: "response 300-499",
res: response
}
}
}
/**
* request log
*/
function _requestLog(req, describe = null) {
if (process.env.NODE_ENV === 'development') {}
}
/**
* response log
*/
function _responseLog(res, conf = {}, describe = null) {
let _statusCode = res.statusCode;
if (process.env.NODE_ENV === 'development') {}
if (_statusCode === 500) {
// save log to server
}
}
/**
* 请求方法封装
*/
export const req = new Request(config, reqInterceptor, resInterceptor)

97
common/js/request.js Normal file
View File

@@ -0,0 +1,97 @@
import { requestConfig } from './common.js';
export default class Request {
// 构造方法
constructor(config = {}, reqInterceptor = null, resInterceptor = null, successHandler, failHandler = null, completeHandler = null){
// 基础属性
this.baseUrl = config.baseUrl
this.header = config.header || {
"Content-Type":"application/x-www-form-urlencoded"
}
// 自定义响应处理器
this.success = successHandler
this.fail = failHandler
this.complete = completeHandler
// 自定义拦截器(请求、响应)
this.requestInterceptor = reqInterceptor
this.responseInterceptor = resInterceptor
}
// 请求type request/upload/download
async request(options, successHandler = null, failHandler = null, completeHandler = null){
const task = options.task || false
const type = options.type || "request"
// delete options.task
let config = null
try {
config = await requestConfig(this, options, successHandler, failHandler, completeHandler)
} catch (e) {
// reject the error
return Promise.reject(e)
}
if(!config || typeof config != 'object'){
return Promise.reject({})
}
const _this = this
if(task){
config["success"] = (response) => {
if (_this.responseInterceptor) {
_this.responseInterceptor(response, config)
}
_this.success && _this.success(response)
successHandler && successHandler(response)
}
config["fail"] = (response) => {
_this.fail && _this.fail(response)
failHandler && failHandler(response)
}
config["complete"] = (response) => {
_this.complete && _this.complete(response)
completeHandler && completeHandler(response)
}
if (type === "request"){
return uni.request(config)
}else if(type === "upload"){
return uni.uploadFile(config)
}else{
return uni.downloadFile(config)
}
return
}
return new Promise((resolve, reject) => {
config["success"] = (response) => {
let _res = null
if(_this.responseInterceptor){
_res = _this.responseInterceptor(response, config)
}
_this.success && _this.success(response)
successHandler && successHandler(response)
// 为了对reject的内容更加可控增加了一个控制字段 wakaryReqToReject
if(_res.wakaryReqToReject){
delete _res.wakaryReqToReject
reject(_res)
}else{
resolve(_res)
}
}
config["fail"] = (error) => {
_this.fail && _this.fail(error)
failHandler && failHandler(error)
reject(error)
}
config["complete"] = (response) => {
_this.complete && _this.complete(response)
completeHandler && completeHandler(response)
}
if( type === "request"){
uni.request(config)
} else if(type === "upload"){
uni.uploadFile(config)
} else {
uni.downloadFile(config)
}
})
}
}

51
common/js/timeFormat.js Normal file
View File

@@ -0,0 +1,51 @@
// padStart 的 polyfill因为某些机型或情况还无法支持es7的padStart比如电脑版的微信小程序
// 所以这里做一个兼容polyfill的兼容处理
if (!String.prototype.padStart) {
// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
String.prototype.padStart = function(maxLength, fillString = ' ') {
if (Object.prototype.toString.call(fillString) !== "[object String]") throw new TypeError(
'fillString must be String')
let str = this
// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
if (str.length >= maxLength) return String(str)
let fillLength = maxLength - str.length,
times = Math.ceil(fillLength / fillString.length)
while (times >>= 1) {
fillString += fillString
if (times === 1) {
fillString += fillString
}
}
return fillString.slice(0, fillLength) + str;
}
}
// 其他更多是格式化有如下:
// yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合
function timeFormat(dateTime = null, fmt = 'yyyy-mm-dd') {
// 如果为null,则格式化当前时间
if (!dateTime) dateTime = Number(new Date());
// 如果dateTime长度为10或者13则为秒和毫秒的时间戳如果超过13位则为其他的时间格式
if (dateTime.toString().length == 10) dateTime *= 1000;
let date = new Date(dateTime);
let ret;
let opt = {
"y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"h+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"s+": date.getSeconds().toString() // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
};
};
return fmt;
}
export default timeFormat

75
common/js/tool.js Normal file
View File

@@ -0,0 +1,75 @@
// 时间格式
const commonTimeAgo = num => {
if (num) {
var t = new Date().getTime() - Date.parse(num.replace(/-/gi, "/")),
n = t / 31104e6,
a = t / 2592e6,
r = t / 6048e5,
o = t / 864e5,
i = t / 36e5,
u = t / 6e4;
return n >= 1 ? parseInt(n) + "年前" : a >= 1 ? parseInt(a) + "个月前" : r >= 1 ? parseInt(r) + "周前" : o >= 1 ?
parseInt(o) + "天前" : i >= 1 ? parseInt(i) + "小时前" : u >= 1 ? parseInt(u) + "分钟前" : "刚刚";
}
}
//判断闰年代码
function isLeapYear(Year) {
if (((Year % 4) == 0) && ((Year % 100) != 0) || ((Year % 400) == 0)) {
return true;
} else {
return false;
}
}
//计算某月份天数
function TotalDays(year, month) {
var days = 0;
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
days += 31;
break;
case 4:
case 6:
case 9:
case 11:
days += 30;
break;
case 2:
if (isLeapYear(year)) {
days += 29;
} else {
days += 28;
}
break;
}
return days;
}
/**
* @param {Object} mobile
* 手机号加密
*/
function encode(mobile) {
if (mobile.length == 13) {
let pre = mobile.slice(0, 3); // 前三位
let mid = '****';
let suf = mobile.slice(-4); // 后四位
return `${pro}${mid}${suf}`;
} else {
return mobile
}
}
module.exports = {
commonTimeAgo,
isLeapYear,
TotalDays,
encode
}

300
common/js/util.js Normal file
View File

@@ -0,0 +1,300 @@
let systemInfo = uni.getSystemInfoSync();
/**
* 更新小程序
*/
function updateMiniapp() {
const updateManager = uni.getUpdateManager();
updateManager.onCheckForUpdate(function(res) {
// 请求完新版本信息的回调
});
updateManager.onUpdateReady(function(res) {
uni.showModal({
title: "更新提示",
content: "新版本已经准备好,是否重启应用?",
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
},
});
});
updateManager.onUpdateFailed(function(res) {
// 新的版本下载失败
});
}
/**
* 获取AppInfo
*/
async function getAppInfo() {
const accountInfo = await uni.getAccountInfoSync();
const appId = await accountInfo.miniProgram.appId;
return appId;
}
/**
* 获取code
*/
async function getCode() {
return new Promise((resolve, reject) => {
uni.getProvider({
service: "oauth",
success: (res) => {
// 平台来源
uni.login({
provider: res.provider[0],
scopes: ["auth_base"],
success(loginRes) {
let code = loginRes.code;
resolve(code);
},
fail(err) {
uni.showModal({
title: err.errMsg,
showCancel: false,
});
},
complete() {},
});
},
});
});
}
//校验手机号有效性
const checkPhoneNumber = mobile => {
//验证手机号真实性
if (mobile == null || mobile == undefined) {
wx.showToast({
title: '手机号不能为空',
icon: 'none'
})
return false
}
// 匹配所有号码(手机卡 + 数据卡 + 上网卡)
if (!(/^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[01356789]\d{2}|4(?:0\d|1[0-2]|9\d))|9[01356789]\d{2}|6[2567]\d{2}|4(?:[14]0\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/
.test(mobile))) {
wx.showToast({
title: '手机号格式错误',
icon: 'none'
})
return false
}
return true
}
let m_checkProv = (val) => {
var pattern = /^[1-9][0-9]/;
var provs = {
11: "北京",
12: "天津",
13: "河北",
14: "山西",
15: "内蒙古",
21: "辽宁",
22: "吉林",
23: "黑龙江 ",
31: "上海",
32: "江苏",
33: "浙江",
34: "安徽",
35: "福建",
36: "江西",
37: "山东",
41: "河南",
42: "湖北 ",
43: "湖南",
44: "广东",
45: "广西",
46: "海南",
50: "重庆",
51: "四川",
52: "贵州",
53: "云南",
54: "西藏 ",
61: "陕西",
62: "甘肃",
63: "青海",
64: "宁夏",
65: "新疆",
71: "台湾",
81: "香港",
82: "澳门"
};
if (pattern.test(val)) {
if (provs[val]) {
return true;
}
}
return false;
};
let m_checkDate = (val) => {
var pattern = /^(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)$/;
if (pattern.test(val)) {
var year = val.substring(0, 4);
var month = val.substring(4, 6);
var date = val.substring(6, 8);
var date2 = new Date(year + "-" + month + "-" + date);
if (date2 && date2.getMonth() == (parseInt(month) - 1)) {
return true;
}
}
return false;
};
let m_checkCode = (val) => {
var p = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
var parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2];
var code = val.substring(17);
if (p.test(val)) {
var sum = 0;
for (var i = 0; i < 17; i++) {
sum += val[i] * factor[i];
}
if (parity[sum % 11] == code.toUpperCase()) {
return true;
}
}
return false;
};
function m_by_checkID(val = '') {
if (m_checkCode(val) && val) {
var date = val.substring(6, 14);
if (m_checkDate(date)) {
if (m_checkProv(val.substring(0, 2))) {
return true;
}
}
}
return false;
};
function trim(str, pos = 'both') {
if (pos == 'both') {
return str.replace(/^\s+|\s+$/g, "");
} else if (pos == "left") {
return str.replace(/^\s*/, '');
} else if (pos == 'right') {
return str.replace(/(\s*$)/g, "");
} else if (pos == 'all') {
return str.replace(/\s+/g, "");
} else {
return str;
}
}
/**
* 样式转换
* 对象转字符串,或者字符串转对象
* @param {Object | String} 需要转换的目标
* @param {String} 转换的目的object-转为对象string-转为字符串
*/
function addStyle(customStyle, target = 'object') {
// 字符串转字符串,对象转对象情形,直接返回
if (uni.$u.test.empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target ===
'string' &&
typeof(customStyle) === 'string') {
return customStyle
}
// 字符串转对象
if (target === 'object') {
// 去除字符串样式中的两端空格(中间的空格不能去掉比如padding: 20px 0如果去掉了就错了),空格是无用的
customStyle = trim(customStyle)
// 根据";"将字符串转为数组形式
const styleArray = customStyle.split(';')
const style = {}
// 历遍数组,拼接成对象
for (let i = 0; i < styleArray.length; i++) {
// 'font-size:20px;color:red;',如此最后字符串有";"的话会导致styleArray最后一个元素为空字符串这里需要过滤
if (styleArray[i]) {
const item = styleArray[i].split(':')
style[trim(item[0])] = trim(item[1])
}
}
return style
}
// 这里为对象转字符串形式
let string = ''
for (const i in customStyle) {
// 驼峰转为中划线的形式否则css内联样式无法识别驼峰样式属性名
const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
string += `${key}:${customStyle[i]};`
}
// 去除两端空格
return trim(string)
}
// 导航栏高度
function navbarHeight(height = '') {
// #ifdef APP-PLUS || H5
return height ? height : 44;
// #endif
// #ifdef MP
// 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
// 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
// return menuButtonInfo.height + (menuButtonInfo.top - this.statusBarHeight) * 2;//导航高度
let he = systemInfo.platform == 'ios' ? 44 : 48;
return height ? height : he;
// #endif
}
// 导航栏高度
function navbarHeight(height = '') {
// #ifdef APP-PLUS || H5
return height ? height : 44;
// #endif
// #ifdef MP
// 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
// 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
// return menuButtonInfo.height + (menuButtonInfo.top - this.statusBarHeight) * 2;//导航高度
let he = systemInfo.platform == 'ios' ? 44 : 48;
return height ? height : he;
// #endif
}
function getAge(identityCard) {
var len = (identityCard + "").length;
if (len == 0) {
return '';
} else {
if ((len != 15) && (len != 18)) //身份证号码只能为15位或18位其它不合法
{
return '';
}
}
var strBirthday = "";
if (len == 18) //处理18位的身份证号码从号码中得到生日和性别代码
{
strBirthday = identityCard.substr(6, 4) + "/" + identityCard.substr(10, 2) + "/" + identityCard.substr(
12, 2);
}
if (len == 15) {
strBirthday = "19" + identityCard.substr(6, 2) + "/" + identityCard.substr(8, 2) + "/" + identityCard
.substr(10, 2);
}
//时间字符串里,必须是“/”
var birthDate = new Date(strBirthday);
var nowDateTime = new Date();
var age = nowDateTime.getFullYear() - birthDate.getFullYear();
//再考虑月、天的因素;.getMonth()获取的是从0开始的这里进行比较不需要加1
if (nowDateTime.getMonth() < birthDate.getMonth() || (nowDateTime.getMonth() == birthDate.getMonth() &&
nowDateTime.getDate() < birthDate.getDate())) {
age--;
}
return age;
}
module.exports = {
getAge,
updateMiniapp,
getAppInfo,
getCode,
checkPhoneNumber,
m_by_checkID,
addStyle,
navbarHeight
};

14
common/messageType.js Normal file
View File

@@ -0,0 +1,14 @@
export const messageType = {
"desc": '病情描述信息',
"text_a": '基本健康信息',
"images": '图片信息',
"record": "就诊记录",
"system": "系统信息",
"end": "系统信息",
"text": "文本信息",
"voice": "语音信息",
"img": "图片信息",
"video": "视频信息",
"doctorCcard":'[医生名片]',
"prescription":'[处方]'
}

27
common/share.js Normal file
View File

@@ -0,0 +1,27 @@
export default {
data() {
return {
// 默认的全局分享内容
share: {
title: '萧康云医',
path: '/pages/login/index', // 全局分享的路径,比如 首页
}
}
},
onShareAppMessage(res) {
return {
title: this.share.title,
path: this.share.path,
}
},
onShareTimeline() {
let that = this;
var url = "/pages/login/index"
return {
title: '萧康云医',
path: url,
query: "", //你的参数拼接,注意:不需要加?
imageUrl: '' //你分享的封面
};
},
}

213
common/utils.js Normal file
View File

@@ -0,0 +1,213 @@
import $store from '@/store/index.js';
import pinyin from './getPingYin.js';
class utils {
constructor(arg) {
}
//返回上一页
back(delta = 1){
uni.navigateBack({
delta:delta
})
}
//获取好友备注,没有返回昵称
getNickName(friend_id,nickname){
var friendIds = uni.getStorageSync("friendIds");
if(friendIds.length){
let petName = nickname;
friendIds.forEach((item)=>{
if(friend_id == item.friend_id){
if(item.pet_name != undefined){
petName = item.pet_name;
}
}
})
return petName;
}else{
return nickname;
}
}
getGroupById(id){
let groups = uni.getStorageSync("groupList");
let info = [];
if(groups && groups.length){
groups.forEach((item)=>{
if(id == item._id){
info = item;
}
})
}
return info;
}
getGroupAvatarList(id){
var avatarList = [];
if(id){
let groups = this.getGroupById(id);
if(groups.group_users != undefined && groups.group_users.length){
groups.group_users.forEach((item)=>{
avatarList.push(item.avatar)
})
}
}
return avatarList;
}
getFriendInfoById(id){
let friends = $store.state.user.friendList;
let info = [];
if(friends.length){
friends.forEach((item)=>{
if(id == item._id){
info = item;
}
})
}
return info;
}
//获取缓存头像
getImageCache(url) {
return url;
let key = `images_cache`;
let list = uni.getStorageSync(key);
list = list ? list : [];
let index = list.findIndex(item => item.url === url)
if (index !== -1) {
return list[index]['cache'];
} else {
var cache = {
url: url,
cache: url,
};
uni.downloadFile({
url: url,
success: (res) => {
if (res.statusCode === 200) {
cache.cache = res.tempFilePath;
list.push(cache);
uni.setStorageSync(key, list);
}
},
fail: (e) => {
}
});
return cache.cache;
}
}
//好友列表按首字母排序
sortFriendList(data) {
var friendList = data;
var letter = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z" ,"#"
];
var indexData = [];
letter.forEach((letterItem, letterIndex) => {
var letterData = {
letter: letterItem,
data: []
}
friendList.forEach((friendListItem, friendListIndex) => {
var firstLetter = friendListItem.nickname.substr(0, 1);
var p = /[a-z]/i;
var b = p.test(firstLetter);
if(b){
var firstWord = firstLetter.toUpperCase();
}else{
var firstWord = pinyin.initial(firstLetter);
}
var p2 = /[A-Z]/i;
var b2 = p2.test(firstWord);
if(!b2 && letterItem === "#"){
letterData.data.push(friendListItem);
}
if (b2 && letterItem === firstWord) {
letterData.data.push(friendListItem);
}
})
if (letterData.data.length > 0) {
indexData.push(letterData);
}
})
return indexData;
}
//格式化时间
formatChatTime(time) {
time = this.dateFormat("YY-mm-dd HH:MM:SS", new Date(time));
var date = time.toString();
var year = date.split("-")[0];
var month = date.split("-")[1];
var day = date.split("-")[2];
var d1 = new Date(year + '/' + month + '/' + day.split(" ")[0]);
var d3 = new Date(date.replace(/-/g, "/"));
var dd = new Date();
var y = dd.getFullYear();
var m = dd.getMonth() + 1;
var d = dd.getDate();
var d2 = new Date(y + '/' + m + '/' + d);
var iday = parseInt(d2 - d1) / 1000 / 60 / 60 / 24;
var hours = d3.getHours();
var minutes = d3.getMinutes();
if (minutes < 10) {
minutes = '0' + minutes;
}
if (hours < 10) {
hours = '0' + hours;
}
if (iday == 0) {
if (hours >= 12) {
return "下午 " + hours + ":" + minutes;
} else {
return "上午 " + hours + ":" + minutes;;
}
} else if (iday == 1) {
var dt = "";
if (hours >= 12) {
dt = "下午 " + hours + ":" + minutes;
} else {
dt = "上午 " + hours + ":" + minutes;;
}
return "昨天 " + dt;
} else if (iday == 2) {
var dt = "";
if (hours >= 12) {
dt = "下午 " + hours + ":" + minutes;
} else {
dt = "上午 " + hours + ":" + minutes;;
}
return "前天 " + dt;
} else {
return year + '/' + month + "/" + d1.getDate()
}
}
dateFormat(fmt, date) {
let ret;
const opt = {
"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"S+": date.getSeconds().toString() // 秒
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
};
};
return fmt;
}
}
export default utils

View File

@@ -0,0 +1,16 @@
<template>
<view class="flex-col flex-ali-center state">
<image src="../../static/image/sf.png"></image>
<d-text text="您的认证信息已提交!" className="tips-c m-t-10 m-t-3"></d-text>
<d-text text="我们会尽快审核,请耐心等待" className="tips-c m-t-1"></d-text>
</view>
</template>
<style lang="scss" scoped>
.state {
margin-top: 160rpx;
image {
width: 300rpx;
height: 223rpx;
}
}
</style>

View File

@@ -0,0 +1,325 @@
<template>
<view class="p-row-32 white">
<movable-area :style="{ height: `${areaHeight}px`}">
<block v-for="(item, index) in list" :key="index">
<movable-view :key="item.sign" @change="e => change(e, index)" @touchend="touchend"
@touchstart="touchstart($event);touchStart(index)" class="drag_item"
:y="item.y" direction="vertical" :disabled='disabled' :style="{zIndex:`${current==index?2:1}`}">
<u-swipe-action :disabled="isSlide" :show="item.show" :index="index" :key="item.sign" @click="click"
@open="open" :options="options" :style="{zIndex:list.length-index}">
<view class="touch_view"
:style="{minHeight: item.itemHeight?item.itemHeight+'px':itemHeight+'px',borderBottom: (index+1)!=list.length?'1px solid #E8E9EB':'1px solid #fff',paddingTop: ifCustomHeight?'48rpx':'32rpx'}"
@click="$emit('click',item)">
<view class="left">
<text>{{ item[name]}}</text>
</view>
<view class="right">
<u-icon size="40" color="#B7B7BA" name="list"></u-icon>
</view>
</view>
</u-swipe-action>
</movable-view>
</block>
</movable-area>
</view>
</template>
<script>
export default {
props: {
ifCustomHeight: {
type: Boolean,
default: false
},
propList: {
type: Array,
default: () => []
},
isSlide: {
type: Boolean,
default: false
},
height: {
type: Number,
default: 104
},
name: {
type: String,
default: 'name'
},
},
data() {
return {
longpress: false,
oldIndex: -1, //记录移动前的位置,为了和移动后进行比较是否发生了位移
newIndex: -1, //当前移动到的位置
direction: '',
current: -1,
itemHeight: uni.upx2px(this.height),
difference: 0, //记录移动前的位置
disabled: true, //是否可以拖动
list: [], //渲染的list
options: [{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}],
timer: null,
startX: 0, //touchStart开始坐标
startY: 0,
};
},
created() {
this.ifCustomHeight && this.getElRect()
},
computed: {
areaHeight() {
let height = this.list.reduce((to, item) => {
to += (item.itemHeight || this.itemHeight)
return to
}, 0)
return height
},
},
watch: {
propList: {
handler(val) {
this.list = this.updateList(val);
this.cloneList = JSON.parse(JSON.stringify(this.list));
},
immediate: true,
deep: true
}
},
methods: {
// 计算滑动角度
touchstart(e) {
this.startX = e.changedTouches[0].clientX,
this.startY = e.changedTouches[0].clientY
},
//计算滑动角度 start 起点坐标 end 终点坐标
angle(start, end) {
var _X = end.X - start.X,
_Y = end.Y - start.Y;
//返回角度 Math.atan()返回数字的反正切值
return 360 * Math.atan(_Y / _X) / (2 * Math.PI);
},
getDirection(e) {
let {
startX,
startY
} = this;
let slidingRange = 45;
let touchMoveX = e.changedTouches[0].clientX;
let touchMoveY = e.changedTouches[0].clientY;
let angle = this.angle({
X: startX,
Y: startY
}, {
X: touchMoveX,
Y: touchMoveY
});
//为了方便计算取绝对值判断
if (Math.abs(angle) > slidingRange && touchMoveY < startY) {
// 向上滑动
this.direction = 'up'
};
if (Math.abs(angle) > slidingRange && touchMoveY > startY) {
// 向下滑动
this.direction = 'down'
}
},
// 获取一个目标元素的高度
getElRect() {
let query = uni.createSelectorQuery().in(this);
query.selectAll('.drag_item').boundingClientRect(res => {
// 如果节点尚未生成res值为null循环调用执行
if (res.length == 0) {
setTimeout(() => {
this.getElRect();
}, 10);
return;
}
for (let i = 0; i < res.length; i++) {
this.$set(this.list[i], 'itemHeight', res[i].height < this.itemHeight ? this.itemHeight :
res[i].height)
this.$set(this.list[i], 'y', this.getTop(this.list, i))
}
this.updateList(this.list)
this.cloneList = JSON.parse(JSON.stringify(this.list));
}).exec();
},
getTop(list = [], end = 0) {
let itemHeight = 0
for (let i = 0; i < list.length; i++) {
if (end == i) {
break
}
itemHeight += list[i]['itemHeight']
}
return itemHeight
},
// 添加或更新数组y值
updateList(list = []) {
return list.map((item, index) => {
let obj = {
...item,
show: false,
sign: Math.random() + index //防止不更新的情况和标识位
}
if (this.ifCustomHeight) {
return obj;
} else {
return {
...obj,
y: index * this.itemHeight
};
}
});
},
touchStart(index) {
this.disabled = false
this.current = index;
this.oldIndex = index;
},
change(e, index) {
// 不是当前元素位移直接不处理
if (index != this.current) return;
this.longpress = true
if (this.ifCustomHeight) {
this.difference = e.detail.y;
} else {
//位移的距离px
let difference = e.detail.y - this.current * this.itemHeight;
//计算位移几个位置
let tempIndex = difference > 0 ? parseInt((difference + (this.itemHeight / 2)) / this.itemHeight) :
parseInt((difference - (this.itemHeight / 2)) / this.itemHeight);
//当前拖到的位置
this.newIndex = Math.abs(this.current + tempIndex);
if (this.newIndex > -1 && this.oldIndex > -1 && this.newIndex !== this.oldIndex) {
this.changeList();
}
}
},
changeList() {
// 没想到怎么改变原数组, 直接拷贝一份数组出来,不考虑情况 一把梭 直接遍历拷贝的原数组.
//直接把改变位置赋值回去不行不知道为什么只能改变每一项的y值
let arr = JSON.parse(JSON.stringify(this.cloneList));
arr.splice(this.newIndex, 0, ...arr.splice(this.current, 1));
this.list.forEach((item, index) => {
if (index !== this.current) {
item.y = arr.findIndex(citem => citem.sign == item.sign) * this.itemHeight;
}
});
this.oldIndex = this.newIndex;
},
touchend(e) {
if (!this.longpress) {
return
}
if (this.ifCustomHeight) {
if (!this.list[this.current] || this.current < 0) {
return
}
this.getDirection(e)
let itemHeight = this.list[this.current]['itemHeight'];
console.log(this.difference);
let difference = this.difference;
let arr = JSON.parse(JSON.stringify(this.cloneList));
let index = this.current;
for (var i = 0; i < arr.length; i++) {
let item = arr[i]['y'] + arr[i]['itemHeight'] / 2
if (this.direction == 'down' && difference + itemHeight >= item) {
index = i
} else if (this.direction == 'up' && item >= difference) {
index = i
break
}
}
index >= 0 && arr.splice(index, 0, ...arr.splice(this.current, 1));
for (var i = 0; i < arr.length; i++) {
arr[i]['y'] = this.getTop(arr, i)
}
this.list = this.updateList(arr);
} else {
if (this.newIndex > -1 && this.current > -1 && this.newIndex !== this.current) {
this.cloneList.splice(this.newIndex, 0, ...this.cloneList.splice(this.current, 1));
this.list = this.updateList(this.cloneList);
}
}
this.cloneList = JSON.parse(JSON.stringify(this.list));
this.emitChang(this.list)
this.current = -1;
this.oldIndex = -1;
this.newIndex = -1;
this.disabled = true
this.difference=0
this.longpress = false
},
click(index, index1) {
if (index1 == 0) {
this.$emit('del', index)
}
},
// 如果打开一个的时候,不需要关闭其他,则无需实现本方法
open(index) {
// 先将正在被操作的swipeAction标记为打开状态否则由于props的特性限制
// 原本为'false',再次设置为'false'会无效
this.list[index].show = true;
this.list.map((val, idx) => {
if (index != idx) this.list[idx].show = false;
});
},
emitChang(arr) {
let tempArr = JSON.parse(JSON.stringify(arr))
tempArr.forEach(item => {
delete item.y
delete item.show
delete item.sign
})
this.$emit('change', tempArr)
}
}
};
</script>
<style lang="scss">
.drag_item {
width: 686rpx;
background-color: #fff;
height: max-content;
}
movable-area {
width: 686rpx;
background-color: #fff;
}
.touch_view {
width: 686rpx;
display: flex;
justify-content: space-between;
align-items: flex-start;
.left {
display: flex;
align-items: center;
padding-bottom: 16rpx;
padding-right: 16rpx;
text {
font-size: 28rpx;
color: #31353D;
}
}
.right {
width: 40rpx;
height: 40rpx;
background: #FFFFFF;
box-shadow: 0px 0px 8rpx 0px rgba(0, 0, 0, 0.08);
border-radius: 4rpx;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
@font-face {
font-family: 'iconfont';
/* Project id 2843597 */
src: url('//at.alicdn.com/t/font_2843597_kx4g4k3cyrh.woff2?t=1633846789896') format('woff2'),
url('//at.alicdn.com/t/font_2843597_kx4g4k3cyrh.woff?t=1633846789896') format('woff'),
url('//at.alicdn.com/t/font_2843597_kx4g4k3cyrh.ttf?t=1633846789896') format('truetype');
}
@font-face {
font-family: 'iconfont2';
/* Project id 3757975 */
src: url('//at.alicdn.com/t/c/font_3757975_9yihr8amvjk.woff2?t=1670469782943') format('woff2'),
url('//at.alicdn.com/t/c/font_3757975_9yihr8amvjk.woff?t=1670469782943') format('woff'),
url('//at.alicdn.com/t/c/font_3757975_9yihr8amvjk.ttf?t=1670469782943') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconfont2 {
font-family: "iconfont2" !important;
font-size: 25rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-release:before {
content: "\e61b";
}
.icon-list-check:before {
content: "\e667";
}
.icon-add:before {
content: "\e664";
}
.icon-textformat:before {
content: "\e8b8";
}
.icon-image:before {
content: "\e65b";
}
.icon-photo:before {
content: "\e65f";
}
.icon-keyboard:before {
content: "\e64c";
}
.icon-undo:before {
content: "\e6f0";
}
.icon-redo:before {
content: "\e6f1";
}
.icon-bgcolors:before {
content: "\eb95";
}
.icon-fontbgcolor:before {
content: "\e68d";
}
.icon-indent:before {
content: "\e7f3";
}
.icon-outdent:before {
content: "\e7f4";
}
.icon-menu:before {
content: "\e7f5";
}
.icon-unorderedlist:before {
content: "\e7f6";
}
.icon-orderedlist:before {
content: "\e7f7";
}
.icon-align-right:before {
content: "\e7f8";
}
.icon-align-center:before {
content: "\e7f9";
}
.icon-align-left:before {
content: "\e7fa";
}
.icon-bold:before {
content: "\e7fb";
}
.icon-font-size:before {
content: "\e7fd";
}
.icon-line-height:before {
content: "\e7fe";
}
.icon-strikethrough:before {
content: "\e7ff";
}
.icon-underline:before {
content: "\e800";
}
.icon-italic:before {
content: "\e801";
}
.icon-check:before {
content: "\e802";
}
.icon-line:before {
content: "\e803";
}

View File

@@ -0,0 +1,14 @@
/**
* 处理html中的图片地址和宽度
* resetClass: 是否将class="editor-- 更改为class="
* EditorContext.setContents时img标签的class会自带一个前缀editor--如原class="editor-img"EditorContext.setContents后class="editor--editor-img" */
export const handleHtmlImage = (html = '', resetClass) => {
var newHtml = html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/ig, function(match, src) {
let result = match
//返回每个匹配的字符串
if(resetClass) result = result.replace(/class=\"(.*)editor--/gi,'class="');
result = result.replace(/\<img/gi, '<img style="max-width:100%;height:auto"');
return result;
});
return newHtml;
}

View File

@@ -0,0 +1,71 @@
<template>
<view class="box">
<image :src="image?image:images[mode]"></image>
<slot v-if="!text">
<view class="hiht">暂无商品<text @click="$emit('click')">添加商品</text></view>
</slot>
<view class="hiht" v-if="text" @click="$emit('click')">{{text}}</view>
</view>
</template>
<script>
/**
* steps 内容为空
* @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景
* @property {String} mode 设置模式默认list
*/
export default {
name: 'd-empty',
props: {
mode: {
type: String,
default: 'list'
},
text: {
type: String,
default: ''
},
image: {
type: String,
default: ''
},
images: {
type: [Object,Array],
default: () => {
return {
'list': '../../static/image/list.png',
'search': '../../static/image/search.png'
}
}
}
},
}
</script>
<style lang="scss" scoped>
.box {
padding: 32rpx 0;
display: flex;
flex-direction: column;
width: 100%;
}
image {
width: 200rpx;
height: 173rpx;
}
.hiht {
display: flex;
justify-content: center;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-top: 32rpx;
text-align: center;
text {
color: #6ACDBB;
}
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<view>
<u-popup v-model="show" mode="bottom" :closeable="true" @close="show=false" close-icon-pos='top-right'
:safe-area-inset-bottom="true" :mask-close-able="false">
<view class="handwritten flex-col flex-ali-center">
<text class="title">签字确认</text>
<view class="">
<canvas :style="{width: '686rpx',height: '480rpx',border:'1px solid #2979FF'}" disable-scroll="true"
data-id="1" @touchstart="penStart" @touchmove="penMove" @touchend="penEnd"
canvas-id="canvas"></canvas>
</view>
<view class="flex-row" style="margin-top: 80rpx;">
<view class="retDraw" @click="retDraw">重写</view>
<view style="width: 343rpx;height: 86rpx;">
<u-button :throttle-time="0" @click="saveCanvas();loading=true" type="primary" :loading="loading"
:custom-style="{borderRadius:'0'}">确定</u-button>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import Mycanvas from "@/common/js/handwriting.js";
export default {
name: 'd-handwritten',
props: {
// 是否显示d-handwritten
value: {
type: Boolean,
default: false
},
},
data() {
return {
show: this.value,
canvas: '',
loading: false
}
},
watch: {
value() {
this.show = this.value
}
},
methods: {
// 清除画布
retDraw() {
this.canvas.retDraw();
},
// 笔迹开始
penStart(event) {
this.canvas.penStart(event);
},
// 笔迹移动
penMove(event) {
this.canvas.penMove(event);
},
// 笔迹结束
penEnd(event) {
this.canvas.penEnd(event);
},
// 保存
async saveCanvas() {
let pic = await this.canvas.saveCanvas(this);
this.loading = false;
this.$emit('saveCanvas', pic)
}
},
onReady() {
this.$nextTick(() => {
this.canvas = new Mycanvas({
lineColor: this.lineColor,
lineSise: this.lineSise,
canvasName: "canvas"
}, this)
})
}
}
</script>
<style lang="scss" scoped>
.handwritten {
width: 750rpx;
background: #FFFFFF;
border-radius: 16rpx 16rpx 0px 0px;
height: calc(840rpx + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom);
}
.title {
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #323233;
margin-top: 30rpx;
margin-bottom: 48rpx;
}
.retDraw {
width: 343rpx;
height: 86rpx;
background: #FFFFFF rgba(41, 121, 255, 0);
font-size: 32rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #2979FF;
line-height: 86rpx;
text-align: center;
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<view class="u-image" @tap="onClick" :style="[wrapStyle, backgroundStyle]">
<image v-if="!isError" :src="src" :mode="mode" @error="onErrorHandler" @load="onLoadHandler"
:lazy-load="lazyLoad" class="u-image__image" :show-menu-by-longpress="showMenuByLongpress" :style="[{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
},wrapStyle]"></image>
<view v-if="showLoading && loading" class="u-image__loading" :style="{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius),
backgroundColor: this.bgColor
}">
<slot v-if="$slots.loading" name="loading" />
<u-icon v-else :name="loadingIcon" :width="width" :height="height"></u-icon>
</view>
<view v-if="showError && isError && !loading" class="u-image__error" :style="{
borderRadius: shape == 'circle' ? '50%' : $u.addUnit(borderRadius)
}">
<slot v-if="$slots.error" name="error" />
<u-icon v-else :name="errorIcon" :width="width" :height="height"></u-icon>
</view>
</view>
</template>
<script>
/**
* Image 图片
* @description 此组件为uni-app的image组件的加强版在继承了原有功能外还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
* @tutorial https://uviewui.com/components/image.html
* @property {String} src 图片地址
* @property {String} mode 裁剪模式,见官网说明
* @property {String | Number} width 宽度单位任意如果为数值则为rpx单位默认100%
* @property {String | Number} height 高度单位任意如果为数值则为rpx单位默认 auto
* @property {String} shape 图片形状circle-圆形square-方形默认square
* @property {String | Number} border-radius 圆角值单位任意如果为数值则为rpx单位默认 0
* @property {Boolean} lazy-load 是否懒加载仅微信小程序、App、百度小程序、字节跳动小程序有效默认 true
* @property {Boolean} show-menu-by-longpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效(默认 false
* @property {String} loading-icon 加载中的图标,或者小图片(默认 photo
* @property {String} error-icon 加载失败的图标,或者小图片(默认 error-circle
* @property {Boolean} show-loading 是否显示加载中的图标或者自定义的slot默认 true
* @property {Boolean} show-error 是否显示加载错误的图标或者自定义的slot默认 true
* @property {Boolean} fade 是否需要淡入效果(默认 true
* @property {String Number} width 传入图片路径时图片的宽度
* @property {String Number} height 传入图片路径时图片的高度
* @property {Boolean} webp 只支持网络资源,只对微信小程序有效(默认 false
* @property {String | Number} duration 搭配fade参数的过渡时间单位ms默认 500
* @event {Function} click 点击图片时触发
* @event {Function} error 图片加载失败时触发
* @event {Function} load 图片加载成功时触发
* @example <u-image width="100%" height="300rpx" :src="src"></u-image>
*/
export default {
name: 'd-image',
props: {
// 图片地址
src: {
type: String,
default: ''
},
// 裁剪模式
mode: {
type: String,
default: 'aspectFill'
},
// 宽度,单位任意
width: {
type: [String, Number],
default: '100%'
},
// 高度,单位任意
height: {
type: [String, Number],
default: 'auto'
},
// 图片形状circle-圆形square-方形
shape: {
type: String,
default: 'square'
},
// 圆角,单位任意
borderRadius: {
type: [String, Number],
default: 0
},
// 是否懒加载微信小程序、App、百度小程序、字节跳动小程序
lazyLoad: {
type: Boolean,
default: true
},
// 开启长按图片显示识别微信小程序码菜单
showMenuByLongpress: {
type: Boolean,
default: true
},
// 加载中的图标,或者小图片
loadingIcon: {
type: String,
default: 'photo'
},
// 加载失败的图标,或者小图片
errorIcon: {
type: String,
default: 'error-circle'
},
// 是否显示加载中的图标或者自定义的slot
showLoading: {
type: Boolean,
default: true
},
// 是否显示加载错误的图标或者自定义的slot
showError: {
type: Boolean,
default: true
},
// 是否需要淡入效果
fade: {
type: Boolean,
default: true
},
// 只支持网络资源,只对微信小程序有效
webp: {
type: Boolean,
default: false
},
// 过渡时间单位ms
duration: {
type: [String, Number],
default: 500
},
// 背景颜色,用于深色页面加载图片时,为了和背景色融合
bgColor: {
type: String,
default: '#f3f4f6'
}
},
data() {
return {
// 图片是否加载错误,如果是,则显示错误占位图
isError: false,
// 初始化组件时,默认为加载中状态
loading: true,
// 不透明度,为了实现淡入淡出的效果
opacity: 1,
// 过渡时间因为props的值无法修改故需要一个中间值
durationTime: this.duration,
// 图片加载完成时去掉背景颜色因为如果是png图片就会显示灰色的背景
backgroundStyle: {}
};
},
watch: {
src: {
immediate: true,
handler(n) {
if (!n) {
// 如果传入null或者''或者false或者undefined标记为错误状态
this.isError = true;
this.loading = false;
} else {
this.isError = false;
}
}
}
},
computed: {
wrapStyle() {
let style = {};
// 通过调用addUnit()方法如果有单位如百分比px单位等直接返回如果是纯粹的数值则加上rpx单位
style.width = this.$u.addUnit(this.width);
style.height = this.$u.addUnit(this.height);
// 如果是配置了圆形设置50%的圆角,否则按照默认的配置值
style.borderRadius = this.shape == 'circle' ? '50%' : this.$u.addUnit(this.borderRadius);
// 如果设置圆角必须要有hidden否则可能圆角无效
style.overflow = this.borderRadius > 0 ? 'hidden' : 'visible';
if (this.fade) {
style.opacity = this.opacity;
style.transition = `opacity ${Number(this.durationTime) / 1000}s ease-in-out`;
}
return style;
}
},
methods: {
// 点击图片
onClick() {
this.$emit('click');
},
// 图片加载失败
onErrorHandler(err) {
this.loading = false;
this.isError = true;
this.$emit('error', err);
},
// 图片加载完成标记loading结束
onLoadHandler() {
this.loading = false;
this.isError = false;
this.$emit('load');
// 如果不需要动画效果,就不执行下方代码,同时移除加载时的背景颜色
// 否则无需fade效果时png图片依然能看到下方的背景色
if (!this.fade) return this.removeBgColor();
// 原来opacity为1(不透明,是为了显示占位图)改成0(透明,意味着该元素显示的是背景颜色,默认的灰色)再改成1是为了获得过渡效果
this.opacity = 0;
// 这里设置为0是为了图片展示到背景全透明这个过程时间为0延时之后延时之后重新设置为duration是为了获得背景透明(灰色)
// 到图片展示的过程中的淡入效果
this.durationTime = 0;
// 延时50ms否则在浏览器H5过渡效果无效
setTimeout(() => {
this.durationTime = this.duration;
this.opacity = 1;
setTimeout(() => {
this.removeBgColor();
}, this.durationTime);
}, 50);
},
// 移除图片的背景色
removeBgColor() {
// 淡入动画过渡完成后将背景设置为透明色否则png图片会看到灰色的背景
this.backgroundStyle = {
backgroundColor: 'transparent'
};
}
}
};
</script>
<style scoped lang="scss">
// 定义混入指令用于在非nvue环境下的flex定义因为nvue没有display属性会报错
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.u-image {
position: relative;
transition: opacity 0.5s ease-in-out;
&__image {
width: 100%;
height: 100%;
}
&__loading,
&__error {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
@include vue-flex;
align-items: center;
justify-content: center;
background-color: $u-bg-color;
color: $u-tips-color;
font-size: 46rpx;
}
}
</style>

View File

@@ -0,0 +1,466 @@
<template>
<view
class="u-input"
:class="{
'u-input--border': border,
'u-input--error': validateState
}"
:style="{
padding: `0 ${border ? 20 : 34}rpx`,
borderColor: borderColor,
textAlign: inputAlign,
borderRadius:borderRadius
}"
@tap.stop="inputClick"
>
<view
class="u-input__prefix-icon"
v-if="prefixIcon || $slots.prefix"
>
<slot name="prefix">
<u-icon
:name="prefixIcon"
:size="prefixIconSize"
:customStyle="prefixIconStyle"
:color="prefixIconColor"
></u-icon>
</slot>
</view>
<textarea
v-if="type == 'textarea'"
class="u-input__input u-input__textarea"
:style="[getStyle]"
:value="defaultValue"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
:maxlength="inputMaxlength"
:fixed="fixed"
:focus="focus"
:autoHeight="autoHeight"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:cursor-spacing="getCursorSpacing"
:show-confirm-bar="showConfirmbar"
@input="handleInput"
@blur="handleBlur"
@focus="onFocus"
@confirm="onConfirm"
/>
<input
v-else
class="u-input__input"
:type="type == 'password' ? 'text' : type"
:style="[getStyle]"
:value="defaultValue"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled || type === 'select'"
:maxlength="inputMaxlength"
:focus="focus"
:confirmType="confirmType"
:cursor-spacing="getCursorSpacing"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:show-confirm-bar="showConfirmbar"
@focus="onFocus"
@blur="handleBlur"
@input="handleInput"
@confirm="onConfirm"
/>
<View class="line" />
<view style="color:#536DFE;" @click="send">
{{codeText}}
</view>
</view>
</template>
<script>
/**
* input 输入框
* @description 此组件为一个输入框默认没有边框和样式是专门为配合表单组件u-form而设计的利用它可以快速实现表单验证输入内容下拉选择等功能。
* @tutorial http://uviewui.com/components/input.html
* @property {String} type 模式选择,见官网说明
* @property {Boolean} clearable 是否显示右侧的清除图标(默认true)
* @property {} v-model 用于双向绑定输入框的值
* @property {String} input-align 输入框文字的对齐方式(默认left)
* @property {String} placeholder placeholder显示值(默认 '请输入内容')
* @property {Boolean} disabled 是否禁用输入框(默认false)
* @property {String Number} maxlength 输入框的最大可输入长度(默认140)
* @property {String Number} selection-start 光标起始位置自动聚焦时有效需与selection-end搭配使用默认-1
* @property {String Number} maxlength 光标结束位置自动聚焦时有效需与selection-start搭配使用默认-1
* @property {String Number} cursor-spacing 指定光标与键盘的距离单位px(默认0)
* @property {String} placeholderStyle placeholder的样式字符串形式如"color: red;"(默认 "color: #c0c4cc;")
* @property {String} confirm-type 设置键盘右下角按钮的文字仅在type为text时生效(默认done)
* @property {Object} custom-style 自定义输入框的样式,对象形式
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} fixed 如果type为textarea且在一个"position:fixed"的区域需要指明为true(默认false)
* @property {Boolean} password-icon type为password时是否显示右侧的密码查看图标(默认true)
* @property {Boolean} border 是否显示边框(默认false)
* @property {String} border-color 输入框的边框颜色(默认#dcdfe6)
* @property {Boolean} auto-height 是否自动增高输入区域type为textarea时有效(默认true)
* @property {String Number} height 高度单位rpx(text类型时为70textarea时为100)
* @example <u-input v-model="value" :type="type" :border="border" />
*/
export default {
name: 'u-input',
props: {
value: {
type: [String, Number],
default: ''
},
// 输入框的类型textareatextnumber
type: {
type: String,
default: 'text'
},
inputAlign: {
type: String,
default: 'left'
},
placeholder: {
type: String,
default: '请输入内容'
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
placeholderStyle: {
type: String,
default: 'color: #c0c4cc;'
},
codeText: {
type: String,
default: '发送验证码'
},
confirmType: {
type: String,
default: 'done'
},
// 输入框的自定义样式
customStyle: {
type: Object,
default() {
return {};
}
},
// 图标的自定义样式
prefixIconStyle: {
type: Object,
default() {
return {};
}
},
// 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true
fixed: {
type: Boolean,
default: false
},
// 是否自动获得焦点
focus: {
type: Boolean,
default: false
},
// 密码类型时,是否显示右侧的密码图标
passwordIcon: {
type: Boolean,
default: true
},
// input|textarea是否显示边框
border: {
type: Boolean,
default: false
},
// 输入框的边框颜色
borderColor: {
type: String,
default: '#dcdfe6'
},
autoHeight: {
type: Boolean,
default: true
},
// type=select时旋转右侧的图标标识当前处于打开还是关闭select的状态
// open-打开close-关闭
selectOpen: {
type: Boolean,
default: false
},
// 高度单位rpx
height: {
type: [Number, String],
default: ''
},
// 是否可清空
clearable: {
type: Boolean,
default: true
},
// 指定光标与键盘的距离,单位 px
cursorSpacing: {
type: [Number, String],
default: 0
},
// 光标起始位置自动聚焦时有效需与selection-end搭配使用
selectionStart: {
type: [Number, String],
default: -1
},
// 光标结束位置自动聚焦时有效需与selection-start搭配使用
selectionEnd: {
type: [Number, String],
default: -1
},
// 是否自动去除两端的空格
trim: {
type: Boolean,
default: true
},
// 是否显示键盘上方带有”完成“按钮那一栏
showConfirmbar:{
type:Boolean,
default:true
},
// 左边图标名称
prefixIcon:{
type:String,
default:''
},
// 左边图标大小
prefixIconSize:{
type:[String,Number],
default:'30rpx'
},
// 左边图标颜色
prefixIconColor:{
type:String,
default:''
},
// 边框圆角
borderRadius:{
type:String,
default:'6rpx'
},
},
data() {
return {
defaultValue: this.value,
inputHeight: 70, // input的高度
textareaHeight: 100, // textarea的高度
validateState: false, // 当前input的验证状态用于错误时边框是否改为红色
focused: false, // 当前是否处于获得焦点的状态
showPassword: false, // 是否预览密码
lastValue: '', // 用于头条小程序,判断@input中前后的值是否发生了变化因为头条中文下按下键没有输入内容也会触发@input时间
};
},
watch: {
value(nVal, oVal) {
this.defaultValue = nVal;
// 当值发生变化且为select类型时(此时input被设置为disabled不会触发@input事件),模拟触发@input事件
if(nVal != oVal && this.type == 'select') this.handleInput({
detail: {
value: nVal
}
})
},
},
computed: {
// 因为uniapp的input组件的maxlength组件必须要数值这里转为数值给用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
getStyle() {
let style = {};
// 如果没有自定义高度就根据type为input还是textare来分配一个默认的高度
style.minHeight = this.height ? this.height + 'rpx' : this.type == 'textarea' ?
this.textareaHeight + 'rpx' : this.inputHeight + 'rpx';
style = Object.assign(style, this.customStyle);
return style;
},
//
getCursorSpacing() {
return Number(this.cursorSpacing);
},
// 光标起始位置
uSelectionStart() {
return String(this.selectionStart);
},
// 光标结束位置
uSelectionEnd() {
return String(this.selectionEnd);
}
},
created() {
// 监听u-form-item发出的错误事件将输入框边框变红色
this.$on('on-form-item-error', this.onFormItemError);
},
methods: {
/**
* 派发 (向上查找) (一个)
* @param componentName // 需要找的组件的名称
* @param eventName // 事件名称
* @param params // 需要传递的参数
*/
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;//$parent 找到最近的父节点 $root 根节点
let name = parent.$options.name; // 获取当前组件实例的name
// 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
// 循环出当前名称的一样的组件实例
while (parent && (!name||name!==componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
// 有节点表示当前找到了name一样的实例
if (parent) {
parent.$emit.apply(parent,[eventName].concat(params))
}
},
/**
* change 事件
* @param event
*/
handleInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if(this.trim) value = this.$u.trim(value);
// vue 原生的方法 return 出去
this.$emit('input', value);
// 当前model 赋值
this.defaultValue = value;
// 过一个生命周期再发送事件给u-form-item否则this.$emit('input')更新了父组件的值,但是微信小程序上
// 尚未更新到u-form-item导致获取的值为空从而校验混论
// 这里不能延时时间太短或者使用this.$nextTick否则在头条上会造成混乱
setTimeout(() => {
// 头条小程序由于自身bug导致中文下每按下一个键(尚未完成输入),都会触发一次@input导致错误这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', value);
}, 40)
},
/**
* blur 事件
* @param event
*/
handleBlur(event) {
// 最开始使用的是监听图标@touchstart事件自从hx2.8.4后,此方法在微信小程序出错
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件导致图标消失而无法点击这里做一个延时
setTimeout(() => {
this.focused = false;
}, 100)
// vue 原生的方法 return 出去
this.$emit('blur', event.detail.value);
setTimeout(() => {
// 头条小程序由于自身bug导致中文下每按下一个键(尚未完成输入),都会触发一次@input导致错误这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-blur', event.detail.value);
}, 40)
},
onFormItemError(status) {
this.validateState = status;
},
onFocus(event) {
this.focused = true;
this.$emit('focus');
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.$emit('input', '');
},
inputClick() {
this.$emit('click');
},
send(e) {
this.$emit('sendCode');
},
}
};
</script>
<style lang="scss" scoped>
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.line{
width: 0px;
height: 58rpx;
opacity: 1;
margin: 0 20rpx;
border: 1rpx solid #D5D9E5;
}
.u-input {
position: relative;
flex: 1;
background-color: #f9fafb;
align-items: center;
@include vue-flex;
&__prefix-icon{
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
&__input {
//height: $u-form-item-height;
font-size: 28rpx;
color: $u-main-color;
flex: 1;
}
&__textarea {
width: auto;
font-size: 28rpx;
color: $u-main-color;
padding: 10rpx 0;
line-height: normal;
flex: 1;
}
&--border {
border-radius: 6rpx;
border-radius: 4px;
border: 1px solid $u-form-item-border-color;
}
&--error {
border-color: $u-type-error!important;
}
&__right-icon {
&__item {
margin-left: 10rpx;
}
&--select {
transition: transform .4s;
&--reverse {
transform: rotate(-180deg);
}
}
}
}
</style>

View File

@@ -0,0 +1,459 @@
<template>
<view
class="u-input"
:class="{
'u-input--border': border,
'u-input--error': validateState
}"
:style="{
padding: `0 ${border ? 20 : 34}rpx`,
borderColor: borderColor,
textAlign: inputAlign,
borderRadius:borderRadius
}"
@tap.stop="inputClick"
>
<view
class="u-input__prefix-icon"
v-if="prefixIcon || $slots.prefix"
>
<slot name="prefix">
<u-icon
:name="prefixIcon"
:size="prefixIconSize"
:customStyle="prefixIconStyle"
:color="prefixIconColor"
></u-icon>
</slot>
</view>
<textarea
v-if="type == 'textarea'"
class="u-input__input u-input__textarea"
:style="[getStyle]"
:value="defaultValue"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled"
:maxlength="inputMaxlength"
:fixed="fixed"
:focus="focus"
:autoHeight="autoHeight"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:cursor-spacing="getCursorSpacing"
:show-confirm-bar="showConfirmbar"
@input="handleInput"
@blur="handleBlur"
@focus="onFocus"
@confirm="onConfirm"
/>
<input
v-else
class="u-input__input"
:type="type == 'password' ? 'text' : type"
:style="[getStyle]"
:value="defaultValue"
:password="type == 'password' && !showPassword"
:placeholder="placeholder"
:placeholderStyle="placeholderStyle"
:disabled="disabled || type === 'select'"
:maxlength="inputMaxlength"
:focus="focus"
:confirmType="confirmType"
:cursor-spacing="getCursorSpacing"
:selection-end="uSelectionEnd"
:selection-start="uSelectionStart"
:show-confirm-bar="showConfirmbar"
@focus="onFocus"
@blur="handleBlur"
@input="handleInput"
@confirm="onConfirm"
/>
<view class="u-input__right-icon u-flex">
<view class="u-input__right-icon__clear u-input__right-icon__item" @tap="onClear" v-if="clearable && value != '' && focused">
<u-icon size="32" name="close-circle-fill" color="#c0c4cc"/>
</view>
<view class="u-input__right-icon__clear u-input__right-icon__item" v-if="passwordIcon && type == 'password'">
<u-icon size="32" :name="!showPassword ? 'eye' : 'eye-fill'" color="#c0c4cc" @click="showPassword = !showPassword"/>
</view>
<view class="u-input__right-icon--select u-input__right-icon__item" v-if="type == 'select'" :class="{
'u-input__right-icon--select--reverse': selectOpen
}">
<u-icon name="arrow-down-fill" size="26" color="#c0c4cc"></u-icon>
</view>
</view>
</view>
</template>
<script>
/**
* input 输入框
* @description 此组件为一个输入框默认没有边框和样式是专门为配合表单组件u-form而设计的利用它可以快速实现表单验证输入内容下拉选择等功能。
* @tutorial http://uviewui.com/components/input.html
* @property {String} type 模式选择,见官网说明
* @property {Boolean} clearable 是否显示右侧的清除图标(默认true)
* @property {} v-model 用于双向绑定输入框的值
* @property {String} input-align 输入框文字的对齐方式(默认left)
* @property {String} placeholder placeholder显示值(默认 '请输入内容')
* @property {Boolean} disabled 是否禁用输入框(默认false)
* @property {String Number} maxlength 输入框的最大可输入长度(默认140)
* @property {String Number} selection-start 光标起始位置自动聚焦时有效需与selection-end搭配使用默认-1
* @property {String Number} maxlength 光标结束位置自动聚焦时有效需与selection-start搭配使用默认-1
* @property {String Number} cursor-spacing 指定光标与键盘的距离单位px(默认0)
* @property {String} placeholderStyle placeholder的样式字符串形式如"color: red;"(默认 "color: #c0c4cc;")
* @property {String} confirm-type 设置键盘右下角按钮的文字仅在type为text时生效(默认done)
* @property {Object} custom-style 自定义输入框的样式,对象形式
* @property {Boolean} focus 是否自动获得焦点(默认false)
* @property {Boolean} fixed 如果type为textarea且在一个"position:fixed"的区域需要指明为true(默认false)
* @property {Boolean} password-icon type为password时是否显示右侧的密码查看图标(默认true)
* @property {Boolean} border 是否显示边框(默认false)
* @property {String} border-color 输入框的边框颜色(默认#dcdfe6)
* @property {Boolean} auto-height 是否自动增高输入区域type为textarea时有效(默认true)
* @property {String Number} height 高度单位rpx(text类型时为70textarea时为100)
* @example <u-input v-model="value" :type="type" :border="border" />
*/
export default {
name: 'u-input',
props: {
value: {
type: [String, Number],
default: ''
},
// 输入框的类型textareatextnumber
type: {
type: String,
default: 'text'
},
inputAlign: {
type: String,
default: 'left'
},
placeholder: {
type: String,
default: '请输入内容'
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
placeholderStyle: {
type: String,
default: 'color: #c0c4cc;'
},
confirmType: {
type: String,
default: 'done'
},
// 输入框的自定义样式
customStyle: {
type: Object,
default() {
return {};
}
},
// 图标的自定义样式
prefixIconStyle: {
type: Object,
default() {
return {};
}
},
// 如果 textarea 是在一个 position:fixed 的区域,需要显示指定属性 fixed 为 true
fixed: {
type: Boolean,
default: false
},
// 是否自动获得焦点
focus: {
type: Boolean,
default: false
},
// 密码类型时,是否显示右侧的密码图标
passwordIcon: {
type: Boolean,
default: true
},
// input|textarea是否显示边框
border: {
type: Boolean,
default: false
},
// 输入框的边框颜色
borderColor: {
type: String,
default: '#dcdfe6'
},
autoHeight: {
type: Boolean,
default: true
},
// type=select时旋转右侧的图标标识当前处于打开还是关闭select的状态
// open-打开close-关闭
selectOpen: {
type: Boolean,
default: false
},
// 高度单位rpx
height: {
type: [Number, String],
default: ''
},
// 是否可清空
clearable: {
type: Boolean,
default: true
},
// 指定光标与键盘的距离,单位 px
cursorSpacing: {
type: [Number, String],
default: 0
},
// 光标起始位置自动聚焦时有效需与selection-end搭配使用
selectionStart: {
type: [Number, String],
default: -1
},
// 光标结束位置自动聚焦时有效需与selection-start搭配使用
selectionEnd: {
type: [Number, String],
default: -1
},
// 是否自动去除两端的空格
trim: {
type: Boolean,
default: true
},
// 是否显示键盘上方带有”完成“按钮那一栏
showConfirmbar:{
type:Boolean,
default:true
},
// 左边图标名称
prefixIcon:{
type:String,
default:''
},
// 左边图标大小
prefixIconSize:{
type:[String,Number],
default:'30rpx'
},
// 左边图标颜色
prefixIconColor:{
type:String,
default:''
},
// 边框圆角
borderRadius:{
type:String,
default:'6rpx'
},
},
data() {
return {
defaultValue: this.value,
inputHeight: 70, // input的高度
textareaHeight: 100, // textarea的高度
validateState: false, // 当前input的验证状态用于错误时边框是否改为红色
focused: false, // 当前是否处于获得焦点的状态
showPassword: false, // 是否预览密码
lastValue: '', // 用于头条小程序,判断@input中前后的值是否发生了变化因为头条中文下按下键没有输入内容也会触发@input时间
};
},
watch: {
value(nVal, oVal) {
this.defaultValue = nVal;
// 当值发生变化且为select类型时(此时input被设置为disabled不会触发@input事件),模拟触发@input事件
if(nVal != oVal && this.type == 'select') this.handleInput({
detail: {
value: nVal
}
})
},
},
computed: {
// 因为uniapp的input组件的maxlength组件必须要数值这里转为数值给用户可以传入字符串数值
inputMaxlength() {
return Number(this.maxlength);
},
getStyle() {
let style = {};
// 如果没有自定义高度就根据type为input还是textare来分配一个默认的高度
style.minHeight = this.height ? this.height + 'rpx' : this.type == 'textarea' ?
this.textareaHeight + 'rpx' : this.inputHeight + 'rpx';
style = Object.assign(style, this.customStyle);
return style;
},
//
getCursorSpacing() {
return Number(this.cursorSpacing);
},
// 光标起始位置
uSelectionStart() {
return String(this.selectionStart);
},
// 光标结束位置
uSelectionEnd() {
return String(this.selectionEnd);
}
},
created() {
// 监听u-form-item发出的错误事件将输入框边框变红色
this.$on('on-form-item-error', this.onFormItemError);
},
methods: {
/**
* 派发 (向上查找) (一个)
* @param componentName // 需要找的组件的名称
* @param eventName // 事件名称
* @param params // 需要传递的参数
*/
dispatch(componentName, eventName, params) {
let parent = this.$parent || this.$root;//$parent 找到最近的父节点 $root 根节点
let name = parent.$options.name; // 获取当前组件实例的name
// 如果当前有节点 && 当前没名称 且 当前名称等于需要传进来的名称的时候就去查找当前的节点
// 循环出当前名称的一样的组件实例
while (parent && (!name||name!==componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
// 有节点表示当前找到了name一样的实例
if (parent) {
parent.$emit.apply(parent,[eventName].concat(params))
}
},
/**
* change 事件
* @param event
*/
handleInput(event) {
let value = event.detail.value;
// 判断是否去除空格
if(this.trim) value = this.$u.trim(value);
// vue 原生的方法 return 出去
this.$emit('input', value);
// 当前model 赋值
this.defaultValue = value;
// 过一个生命周期再发送事件给u-form-item否则this.$emit('input')更新了父组件的值,但是微信小程序上
// 尚未更新到u-form-item导致获取的值为空从而校验混论
// 这里不能延时时间太短或者使用this.$nextTick否则在头条上会造成混乱
setTimeout(() => {
// 头条小程序由于自身bug导致中文下每按下一个键(尚未完成输入),都会触发一次@input导致错误这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', value);
}, 40)
},
/**
* blur 事件
* @param event
*/
handleBlur(event) {
// 最开始使用的是监听图标@touchstart事件自从hx2.8.4后,此方法在微信小程序出错
// 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件导致图标消失而无法点击这里做一个延时
setTimeout(() => {
this.focused = false;
}, 100)
// vue 原生的方法 return 出去
this.$emit('blur', event.detail.value);
setTimeout(() => {
// 头条小程序由于自身bug导致中文下每按下一个键(尚未完成输入),都会触发一次@input导致错误这里进行判断处理
// #ifdef MP-TOUTIAO
if(this.$u.trim(value) == this.lastValue) return ;
this.lastValue = value;
// #endif
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-blur', event.detail.value);
}, 40)
},
onFormItemError(status) {
this.validateState = status;
},
onFocus(event) {
this.focused = true;
this.$emit('focus');
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.$emit('input', '');
},
inputClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.u-input {
position: relative;
flex: 1;
background-color: #f9fafb;
@include vue-flex;
&__prefix-icon{
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
&__input {
//height: $u-form-item-height;
font-size: 28rpx;
color: $u-main-color;
flex: 1;
}
&__textarea {
width: auto;
font-size: 28rpx;
color: $u-main-color;
padding: 10rpx 0;
line-height: normal;
flex: 1;
}
&--border {
border-radius: 6rpx;
border-radius: 4px;
border: 1px solid $u-form-item-border-color;
}
&--error {
border-color: $u-type-error!important;
}
&__right-icon {
&__item {
margin-left: 10rpx;
}
&--select {
transition: transform .4s;
&--reverse {
transform: rotate(-180deg);
}
}
}
}
</style>

View File

@@ -0,0 +1,344 @@
<template>
<view class="u-loading-icon" :style="[$d.addStyle(customStyle)]" :class="[vertical && 'u-loading-icon--vertical']"
v-if="show">
<view v-if="!webviewHide" class="u-loading-icon__spinner" :class="[`u-loading-icon__spinner--${mode}`]"
ref="ani" :style="{
color: color,
width: $u.addUnit(size),
height: $u.addUnit(size),
borderTopColor: color,
borderBottomColor: otherBorderColor,
borderLeftColor: otherBorderColor,
borderRightColor: otherBorderColor,
'animation-duration': `${duration}ms`,
'animation-timing-function': mode === 'semicircle' || mode === 'circle' ? timingFunction : ''
}">
<block v-if="mode === 'spinner'">
<!-- #ifndef APP-NVUE -->
<view v-for="(item, index) in array12" :key="index" class="u-loading-icon__dot">
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<!-- 此组件内部图标部分无法设置宽高即使通过width和height配置了也无效 -->
<loading-indicator v-if="!webviewHide" class="u-loading-indicator" :animating="true" :style="{
color: color,
width: $u.addUnit(size),
height: $u.addUnit(size)
}" />
<!-- #endif -->
</block>
</view>
<text v-if="text" class="u-loading-icon__text" :style="{
fontSize: $u.addUnit(textSize),
color: textColor,
}">{{text}}</text>
</view>
</template>
<script>
import props from './props.js';
// #ifdef APP-NVUE
const animation = weex.requireModule('animation');
// #endif
/**
* loading 加载动画
* @description 警此组件为一个小动画目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。
* @tutorial https://www.uviewui.com/components/loading.html
* @property {Boolean} show 是否显示组件 (默认 true)
* @property {String} color 动画活动区域的颜色,只对 mode = flower 模式有效默认color['u-tips-color']
* @property {String} textColor 提示文本的颜色默认color['u-tips-color']
* @property {Boolean} vertical 文字和图标是否垂直排列 (默认 false )
* @property {String} mode 模式选择,见官网说明(默认 'circle'
* @property {String | Number} size 加载图标的大小单位px (默认 24
* @property {String | Number} textSize 文字大小(默认 15
* @property {String | Number} text 文字内容
* @property {String} timingFunction 动画模式 (默认 'ease-in-out'
* @property {String | Number} duration 动画执行周期时间(默认 1200
* @property {String} inactiveColor mode=circle时的暗边颜色
* @property {Object} customStyle 定义需要用到的外部样式
* @example <u-loading mode="circle"></u-loading>
*/
export default {
name: 'u-loading-icon',
mixins: [props],
data() {
return {
// Array.form可以通过一个伪数组对象创建指定长度的数组
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from
array12: Array.from({
length: 12
}),
// 这里需要设置默认值为360否则在安卓nvue上会延迟一个duration周期后才执行
// 在iOS nvue上则会一开始默认执行两个周期的动画
aniAngel: 360, // 动画旋转角度
webviewHide: false, // 监听webview的状态如果隐藏了页面则停止动画以免性能消耗
loading: false, // 是否运行中针对nvue使用
}
},
computed: {
// 当为circle类型时给其另外三边设置一个更轻一些的颜色
// 之所以需要这么做的原因是比如父组件传了color为红色那么需要另外的三个边为浅红色
// 而不能是固定的某一个其他颜色(因为这个固定的颜色可能浅蓝,导致效果没有那么细腻良好)
otherBorderColor() {
const lightColor = uni.$u.colorGradient(this.color, '#ffffff', 100)[80]
if (this.mode === 'circle') {
return this.inactiveColor ? this.inactiveColor : lightColor
} else {
return 'transparent'
}
// return this.mode === 'circle' ? this.inactiveColor ? this.inactiveColor : lightColor : 'transparent'
}
},
watch: {
show(n) {
// nvue中show为true且为非loading状态就重新执行动画模块
// #ifdef APP-NVUE
if (n && !this.loading) {
setTimeout(() => {
this.startAnimate()
}, 30)
}
// #endif
}
},
mounted() {
this.init()
},
methods: {
init() {
setTimeout(() => {
// #ifdef APP-NVUE
this.show && this.nvueAnimate()
// #endif
// #ifdef APP-PLUS
this.show && this.addEventListenerToWebview()
// #endif
}, 20)
},
// 监听webview的显示与隐藏
addEventListenerToWebview() {
// webview的堆栈
const pages = getCurrentPages()
// 当前页面
const page = pages[pages.length - 1]
// 当前页面的webview实例
const currentWebview = page.$getAppWebview()
// 监听webview的显示与隐藏从而停止或者开始动画(为了性能)
currentWebview.addEventListener('hide', () => {
this.webviewHide = true
})
currentWebview.addEventListener('show', () => {
this.webviewHide = false
})
},
// #ifdef APP-NVUE
nvueAnimate() {
// nvue下非spinner类型时才需要旋转因为nvue的spinner类型使用了weex的
// loading-indicator组件自带旋转功能
this.mode !== 'spinner' && this.startAnimate()
},
// 执行nvue的animate模块动画
startAnimate() {
this.loading = true
const ani = this.$refs.ani
if (!ani) return
animation.transition(ani, {
// 进行角度旋转
styles: {
transform: `rotate(${this.aniAngel}deg)`,
transformOrigin: 'center center'
},
duration: this.duration,
timingFunction: this.timingFunction,
// delay: 10
}, () => {
// 每次增加360deg为了让其重新旋转一周
this.aniAngel += 360
// 动画结束后继续循环执行动画需要同时判断webviewHide变量
// nvue安卓页面隐藏后依然会继续执行startAnimate方法
this.show && !this.webviewHide ? this.startAnimate() : this.loading = false
})
}
// #endif
}
}
</script>
<style lang="scss" scoped>
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于uView是基于nvue环境进行开发的此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中需要对元素进行重置为flex-direction: column; 否则可能会表现异常
view,
scroll-view,
swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
/* #endif */
$u-loading-icon-color: #c8c9cc !default;
$u-loading-icon-text-margin-left:4px !default;
$u-loading-icon-text-color:$u-content-color !default;
$u-loading-icon-text-font-size:14px !default;
$u-loading-icon-text-line-height:20px !default;
$u-loading-width:30px !default;
$u-loading-height:30px !default;
$u-loading-max-width:100% !default;
$u-loading-max-height:100% !default;
$u-loading-semicircle-border-width: 2px !default;
$u-loading-semicircle-border-color:transparent !default;
$u-loading-semicircle-border-top-right-radius: 100px !default;
$u-loading-semicircle-border-top-left-radius: 100px !default;
$u-loading-semicircle-border-bottom-left-radius: 100px !default;
$u-loading-semicircle-border-bottom-right-radiu: 100px !default;
$u-loading-semicircle-border-style: solid !default;
$u-loading-circle-border-top-right-radius: 100px !default;
$u-loading-circle-border-top-left-radius: 100px !default;
$u-loading-circle-border-bottom-left-radius: 100px !default;
$u-loading-circle-border-bottom-right-radiu: 100px !default;
$u-loading-circle-border-width:2px !default;
$u-loading-circle-border-top-color:#e5e5e5 !default;
$u-loading-circle-border-right-color:$u-loading-circle-border-top-color !default;
$u-loading-circle-border-bottom-color:$u-loading-circle-border-top-color !default;
$u-loading-circle-border-left-color:$u-loading-circle-border-top-color !default;
$u-loading-circle-border-style:solid !default;
$u-loading-icon-host-font-size:0px !default;
$u-loading-icon-host-line-height:1 !default;
$u-loading-icon-vertical-margin:6px 0 0 !default;
$u-loading-icon-dot-top:0 !default;
$u-loading-icon-dot-left:0 !default;
$u-loading-icon-dot-width:100% !default;
$u-loading-icon-dot-height:100% !default;
$u-loading-icon-dot-before-width:2px !default;
$u-loading-icon-dot-before-height:25% !default;
$u-loading-icon-dot-before-margin:0 auto !default;
$u-loading-icon-dot-before-background-color:currentColor !default;
$u-loading-icon-dot-before-border-radius:40% !default;
.u-loading-icon {
/* #ifndef APP-NVUE */
// display: inline-flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
color: $u-loading-icon-color;
&__text {
margin-left: $u-loading-icon-text-margin-left;
color: $u-loading-icon-text-color;
font-size: $u-loading-icon-text-font-size;
line-height: $u-loading-icon-text-line-height;
}
&__spinner {
width: $u-loading-width;
height: $u-loading-height;
position: relative;
/* #ifndef APP-NVUE */
box-sizing: border-box;
max-width: $u-loading-max-width;
max-height: $u-loading-max-height;
animation: u-rotate 1s linear infinite;
/* #endif */
}
&__spinner--semicircle {
border-width: $u-loading-semicircle-border-width;
border-color: $u-loading-semicircle-border-color;
border-top-right-radius: $u-loading-semicircle-border-top-right-radius;
border-top-left-radius: $u-loading-semicircle-border-top-left-radius;
border-bottom-left-radius: $u-loading-semicircle-border-bottom-left-radius;
border-bottom-right-radius: $u-loading-semicircle-border-bottom-right-radiu;
border-style: $u-loading-semicircle-border-style;
}
&__spinner--circle {
border-top-right-radius: $u-loading-circle-border-top-right-radius;
border-top-left-radius: $u-loading-circle-border-top-left-radius;
border-bottom-left-radius: $u-loading-circle-border-bottom-left-radius;
border-bottom-right-radius: $u-loading-circle-border-bottom-right-radiu;
border-width: $u-loading-circle-border-width;
border-top-color: $u-loading-circle-border-top-color;
border-right-color: $u-loading-circle-border-right-color;
border-bottom-color: $u-loading-circle-border-bottom-color;
border-left-color: $u-loading-circle-border-left-color;
border-style: $u-loading-circle-border-style;
}
&--vertical {
flex-direction: column
}
}
/* #ifndef APP-NVUE */
:host {
font-size: $u-loading-icon-host-font-size;
line-height: $u-loading-icon-host-line-height;
}
.u-loading-icon {
&__spinner--spinner {
animation-timing-function: steps(12)
}
&__text:empty {
display: none
}
&--vertical &__text {
margin: $u-loading-icon-vertical-margin;
color: $u-content-color;
}
&__dot {
position: absolute;
top: $u-loading-icon-dot-top;
left: $u-loading-icon-dot-left;
width: $u-loading-icon-dot-width;
height: $u-loading-icon-dot-height;
&:before {
display: block;
width: $u-loading-icon-dot-before-width;
height: $u-loading-icon-dot-before-height;
margin: $u-loading-icon-dot-before-margin;
background-color: $u-loading-icon-dot-before-background-color;
border-radius: $u-loading-icon-dot-before-border-radius;
content: " "
}
}
}
@for $i from 1 through 12 {
.u-loading-icon__dot:nth-of-type(#{$i}) {
transform: rotate($i * 30deg);
opacity: 1 - 0.0625 * ($i - 1);
}
}
@keyframes u-rotate {
0% {
transform: rotate(0deg)
}
to {
transform: rotate(1turn)
}
}
/* #endif */
</style>

View File

@@ -0,0 +1,65 @@
export default {
props: {
customStyle:{
type:Object,
default:()=>{
return {}
}
},
// 是否显示组件
show: {
type: Boolean,
default: true
},
// 颜色
color: {
type: String,
default: '#909193'
},
// 提示文字颜色
textColor: {
type: String,
default: '#909193'
},
// 文字和图标是否垂直排列
vertical: {
type: Boolean,
default: false
},
// 模式选择circle-圆形spinner-花朵形semicircle-半圆形
mode: {
type: String,
default: 'spinner'
},
// 图标大小单位默认px
size: {
type: [String, Number],
default: 32
},
// 文字大小
textSize: {
type: [String, Number],
default: 15
},
// 文字内容
text: {
type: [String, Number],
default: ''
},
// 动画模式
timingFunction: {
type: String,
default: 'ease-in-out'
},
// 动画执行周期时间
duration: {
type: [String, Number],
default: 1200
},
// mode=circle时的暗边颜色
inactiveColor: {
type: String,
default: ''
}
}
}

View File

@@ -0,0 +1,116 @@
<template>
<d-transition :show="loading" :custom-style="{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: bgColor,
display: 'flex',
}">
<view class="u-loading-page">
<view class="u-loading-page__warpper">
<view class="u-loading-page__warpper__loading-icon">
<image v-if="image" :src="image" class="u-loading-page__warpper__loading-icon__img" mode="widthFit">
</image>
<d-loading-icon v-else :mode="loadingMode" :size="$u.addUnit(iconSize)" :color="loadingColor"></d-loading-icon>
</view>
<slot>
<text class="u-loading-page__warpper__text" :style="{
fontSize: $u.addUnit(fontSize),
color: color,
}">{{ loadingText }}</text>
</slot>
</view>
</view>
</d-transition>
</template>
<script>
import props from "./props.js";
/**
* loadingPage 加载动画
* @description 警此组件为一个小动画目前用在uView的loadmore加载更多和switch开关等组件的正在加载状态场景。
* @tutorial https://www.uviewui.com/components/loading.html
* @property {String | Number} loadingText 提示内容 (默认 '正在加载' )
* @property {String} image 文字上方用于替换loading动画的图片
* @property {String} loadingMode 加载动画的模式circle-圆形spinner-花朵形semicircle-半圆形 (默认 'circle'
* @property {Boolean} loading 是否加载中 (默认 false
* @property {String} bgColor 背景色 (默认 '#ffffff'
* @property {String} color 文字颜色 (默认 '#C8C8C8'
* @property {String | Number} fontSize 文字大小 (默认 19
* @property {String} loadingColor 加载中图标的颜色只能rgb或者十六进制颜色值 (默认 '#C8C8C8'
* @property {Object} customStyle 自定义样式
* @example <u-loading mode="circle"></u-loading>
*/
export default {
name: "u-loading-page",
mixins: [props],
data() {
return {};
},
methods: {},
};
</script>
<style lang="scss" scoped>
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于uView是基于nvue环境进行开发的此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中需要对元素进行重置为flex-direction: column; 否则可能会表现异常
view,
scroll-view,
swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
/* #endif */
$text-color: rgb(200, 200, 200) !default;
$text-size: 19px !default;
$u-loading-icon-margin-bottom: 10px !default;
.u-loading-page {
@include flex(column);
flex: 1;
align-items: center;
justify-content: center;
&__warpper {
margin-top: -150px;
justify-content: center;
align-items: center;
/* #ifndef APP-NVUE */
color: $text-color;
font-size: $text-size;
/* #endif */
@include flex(column);
&__loading-icon {
margin-bottom: $u-loading-icon-margin-bottom;
&__img {
width: 40px;
height: 40px;
}
}
&__text {
font-size: $text-size;
color: $text-color;
}
}
}
</style>

View File

@@ -0,0 +1,48 @@
export default {
props: {
iconSize:{
type: [String, Number],
default: '40'
},
// 提示内容
loadingText: {
type: [String, Number],
default: '正在加载...'
},
// 文字上方用于替换loading动画的图片
image: {
type: String,
default: ''
},
// 加载动画的模式circle-圆形spinner-花朵形semicircle-半圆形
loadingMode: {
type: String,
default: 'circle'
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 背景色
bgColor: {
type: String,
default: 'rgba(0, 0, 0, 0.40)'
},
// 文字颜色
color: {
type: String,
default: '#C8C8C8'
},
// 文字大小
fontSize: {
type: [String, Number],
default: 32
},
// 加载中图标的颜色只能rgb或者十六进制颜色值
loadingColor: {
type: String,
default: '#C8C8C8'
}
}
}

View File

@@ -0,0 +1,422 @@
<template>
<view class="u-number-box">
<view
class="u-number-box__slot"
@tap.stop="clickHandler('minus')"
@touchstart="onTouchStart('minus')"
@touchend.stop="clearTimeout"
v-if="showMinus && $slots.minus"
>
<slot name="minus" />
</view>
<view
v-else-if="showMinus"
class="u-number-box__minus"
@tap.stop="clickHandler('minus')"
@touchstart="onTouchStart('minus')"
@touchend.stop="clearTimeout"
hover-class="u-number-box__minus--hover"
hover-stay-time="150"
:class="{ 'u-number-box__minus--disabled': isDisabled('minus') }"
:style="[buttonStyle('minus')]"
>
<u-icon
name="minus"
:color="isDisabled('minus') ? '#c8c9cc' : '#323233'"
size="15"
bold
:customStyle="iconStyle"
></u-icon>
</view>
<slot name="input">
<input
:disabled="disabledInput || disabled"
:cursor-spacing="getCursorSpacing"
:class="{ 'u-number-box__input--disabled': disabled || disabledInput }"
v-model="currentValue"
class="u-number-box__input"
@blur="onBlur"
@focus="onFocus"
@input="onInput"
type="number"
:style="[inputStyle]"
/>
</slot>
<view
class="u-number-box__slot"
@tap.stop="clickHandler('plus')"
@touchstart="onTouchStart('plus')"
@touchend.stop="clearTimeout"
v-if="showPlus && $slots.plus"
>
<slot name="plus" />
</view>
<view
v-else-if="showPlus"
class="u-number-box__plus"
@tap.stop="clickHandler('plus')"
@touchstart="onTouchStart('plus')"
@touchend.stop="clearTimeout"
hover-class="u-number-box__plus--hover"
hover-stay-time="150"
:class="{ 'u-number-box__minus--disabled': isDisabled('plus') }"
:style="[buttonStyle('plus')]"
>
<u-icon
name="plus"
:color="isDisabled('plus') ? '#c8c9cc' : '#323233'"
size="15"
bold
:customStyle="iconStyle"
></u-icon>
</view>
</view>
</template>
<script>
import props from './props.js';
/**
* numberBox 步进器
* @description 该组件一般用于商城购物选择物品数量的场景。
* @tutorial https://uviewui.com/components/numberBox.html
* @property {String | Number} name 步进器标识符在change回调返回
* @property {String | Number} value 用于双向绑定的值初始化时设置设为默认min值(最小值) (默认 0
* @property {String | Number} min 最小值 (默认 1
* @property {String | Number} max 最大值 (默认 Number.MAX_SAFE_INTEGER
* @property {String | Number} step 加减的步长,可为小数 (默认 1
* @property {Boolean} integer 是否只允许输入整数 (默认 false
* @property {Boolean} disabled 是否禁用,包括输入框,加减按钮 (默认 false
* @property {Boolean} disabledInput 是否禁用输入框 (默认 false
* @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false
* @property {String | Number} inputWidth 输入框宽度单位为px (默认 35
* @property {Boolean} showMinus 是否显示减少按钮 (默认 true
* @property {Boolean} showPlus 是否显示增加按钮 (默认 true
* @property {String | Number} decimalLength 显示的小数位数
* @property {Boolean} longPress 是否开启长按加减手势 (默认 true
* @property {String} color 输入框文字和加减按钮图标的颜色 (默认 '#323233'
* @property {String | Number} buttonSize 按钮大小宽高等于此值单位px输入框高度和此值保持一致 (默认 30
* @property {String} bgColor 输入框和按钮的背景颜色 (默认 '#EBECEE'
* @property {String | Number} cursorSpacing 指定光标于键盘的距离避免键盘遮挡输入框单位px (默认 100
* @property {Boolean} disablePlus 是否禁用增加按钮 (默认 false
* @property {Boolean} disableMinus 是否禁用减少按钮 (默认 false
* @property {Object String} iconStyle 加减按钮图标的样式
*
* @event {Function} onFocus 输入框活动焦点
* @event {Function} onBlur 输入框失去焦点
* @event {Function} onInput 输入框值发生变化
* @event {Function} onChange
* @example <u-number-box v-model="value" @change="valChange"></u-number-box>
*/
export default {
name: 'u-number-box',
mixins: [props],
data() {
return {
// 输入框实际操作的值
currentValue: '',
// 定时器
longPressTimer: null
}
},
watch: {
// 多个值之间只要一个值发生变化都要重新检查check()函数
watchChange(n) {
this.check()
},
// 监听v-mode的变化重新初始化内部的值
value(n) {
if (n !== this.currentValue) {
this.currentValue = this.format(this.value)
}
}
},
computed: {
getCursorSpacing() {
// 判断传入的单位如果为px单位需要转成px
const number = parseInt(this.cursorSpacing)
return /rpx$/.test(String(this.cursorSpacing)) ? uni.upx2px(number) : number
},
// 按钮的样式
buttonStyle() {
return (type) => {
const style = {
backgroundColor: this.bgColor,
height: this.$u.addUnit(this.buttonSize),
color: this.color
}
if (this.isDisabled(type)) {
style.backgroundColor = '#f7f8fa'
}
return style
}
},
// 输入框的样式
inputStyle() {
const disabled = this.disabled || this.disabledInput
const style = {
color: this.color,
backgroundColor: this.bgColor,
height: this.$u.addUnit(this.buttonSize),
width: this.$u.addUnit(this.inputWidth)
}
return style
},
// 用于监听多个值发生变化
watchChange() {
return [this.integer, this.decimalLength, this.min, this.max]
},
isDisabled() {
return (type) => {
if (type === 'plus') {
// 在点击增加按钮情况下判断整体的disabled是否单独禁用增加按钮以及当前值是否大于最大的允许值
return (
this.disabled ||
this.disablePlus ||
this.currentValue >= this.max
)
}
// 点击减少按钮同理
return (
this.disabled ||
this.disableMinus ||
this.currentValue <= this.min
)
}
},
},
mounted() {
this.init()
},
methods: {
init() {
this.currentValue = this.format(this.value)
},
// 格式化整理数据,限制范围
format(value) {
value = this.filter(value)
// 如果为空字符串那么设置为0同时将值转为Number类型
value = value === '' ? 0 : +value
// 对比最大最小值取在min和max之间的值
value = Math.max(Math.min(this.max, value), this.min)
// 如果设定了最大的小数位数使用toFixed去进行格式化
if (this.decimalLength !== null) {
value = value.toFixed(this.decimalLength)
}
return value
},
// 过滤非法的字符
filter(value) {
// 只允许0-9之间的数字"."为小数点,"-"为负数时候使用
value = String(value).replace(/[^0-9.-]/g, '')
// 如果只允许输入整数,则过滤掉小数点后的部分
if (this.integer && value.indexOf('.') !== -1) {
value = value.split('.')[0]
}
return value;
},
check() {
// 格式化了之后,如果前后的值不相等,那么设置为格式化后的值
const val = this.format(this.currentValue);
if (val !== this.currentValue) {
this.currentValue = val
}
},
// 判断是否出于禁止操作状态
// isDisabled(type) {
// if (type === 'plus') {
// // 在点击增加按钮情况下判断整体的disabled是否单独禁用增加按钮以及当前值是否大于最大的允许值
// return (
// this.disabled ||
// this.disablePlus ||
// this.currentValue >= this.max
// )
// }
// // 点击减少按钮同理
// return (
// this.disabled ||
// this.disableMinus ||
// this.currentValue <= this.min
// )
// },
// 输入框活动焦点
onFocus(event) {
this.$emit('focus', {
...event.detail,
name: this.name,
})
},
// 输入框失去焦点
onBlur(event) {
// 对输入值进行格式化
const value = this.format(event.detail.value)
// 发出blur事件
this.$emit(
'blur',{
...event.detail,
name: this.name,
}
)
},
// 输入框值发生变化
onInput(e) {
const {
value = ''
} = e.detail || {}
// 为空返回
if (value === '') return
let formatted = this.filter(value)
// 最大允许的小数长度
if (this.decimalLength !== null && formatted.indexOf('.') !== -1) {
const pair = formatted.split('.');
formatted = `${pair[0]}.${pair[1].slice(0, this.decimalLength)}`
}
formatted = this.format(formatted)
this.emitChange(formatted);
},
// 发出change事件
emitChange(value) {
// 如果开启了异步变更值则不修改内部的值需要用户手动在外部通过v-model变更
if (!this.asyncChange) {
this.$nextTick(() => {
this.$emit('input', value)
this.currentValue = value
this.$forceUpdate()
})
}
this.$emit('change', {
value,
name: this.name,
});
},
onChange() {
const {
type
} = this
if (this.isDisabled(type)) {
return this.$emit('overlimit', type)
}
const diff = type === 'minus' ? -this.step : +this.step
const value = this.format(this.add(+this.currentValue, diff))
this.emitChange(value)
this.$emit(type)
},
// 对值扩大后进行四舍五入,再除以扩大因子,避免出现浮点数操作的精度问题
add(num1, num2) {
const cardinal = Math.pow(10, 10);
return Math.round((num1 + num2) * cardinal) / cardinal
},
// 点击加减按钮
clickHandler(type) {
this.type = type
this.onChange()
},
longPressStep() {
// 每隔一段时间重新调用longPressStep方法实现长按加减
this.clearTimeout()
this.longPressTimer = setTimeout(() => {
this.onChange()
this.longPressStep()
}, 250);
},
onTouchStart(type) {
if (!this.longPress) return
this.clearTimeout()
this.type = type
// 一定时间后,默认达到长按状态
this.longPressTimer = setTimeout(() => {
this.onChange()
this.longPressStep()
}, 600)
},
// 触摸结束,清除定时器,停止长按加减
onTouchEnd() {
if (!this.longPress) return
this.clearTimeout()
},
// 清除定时器
clearTimeout() {
clearTimeout(this.longPressTimer)
this.longPressTimer = null
}
}
}
</script>
<style lang="scss" scoped>
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
$u-numberBox-hover-bgColor: #E6E6E6 !default;
$u-numberBox-disabled-color: #c8c9cc !default;
$u-numberBox-disabled-bgColor: #f7f8fa !default;
$u-numberBox-plus-radius: 4px !default;
$u-numberBox-minus-radius: 4px !default;
$u-numberBox-input-text-align: center !default;
$u-numberBox-input-font-size: 15px !default;
$u-numberBox-input-padding: 0 !default;
$u-numberBox-input-margin: 0 2px !default;
$u-numberBox-input-disabled-color: #c8c9cc !default;
$u-numberBox-input-disabled-bgColor: #f2f3f5 !default;
.u-number-box {
@include flex(row);
align-items: center;
&__slot {
/* #ifndef APP-NVUE */
touch-action: none;
/* #endif */
}
&__plus,
&__minus {
width: 35px;
@include flex;
justify-content: center;
align-items: center;
/* #ifndef APP-NVUE */
touch-action: none;
/* #endif */
&--hover {
background-color: $u-numberBox-hover-bgColor !important;
}
&--disabled {
color: $u-numberBox-disabled-color;
background-color: $u-numberBox-disabled-bgColor;
}
}
&__plus {
border-top-right-radius: $u-numberBox-plus-radius;
border-bottom-right-radius: $u-numberBox-plus-radius;
}
&__minus {
border-top-left-radius: $u-numberBox-minus-radius;
border-bottom-left-radius: $u-numberBox-minus-radius;
}
&__input {
position: relative;
text-align: $u-numberBox-input-text-align;
font-size: $u-numberBox-input-font-size;
padding: $u-numberBox-input-padding;
margin: $u-numberBox-input-margin;
@include flex;
align-items: center;
justify-content: center;
&--disabled {
color: $u-numberBox-input-disabled-color;
background-color: $u-numberBox-input-disabled-bgColor;
}
}
}
</style>

View File

@@ -0,0 +1,109 @@
export default {
props: {
// 步进器标识符在change回调返回
name: {
type: [String, Number],
default: ''
},
// 用于双向绑定的值初始化时设置设为默认min值(最小值)
value: {
type: [String, Number],
default: 0
},
// 最小值
min: {
type: [String, Number],
default: 1
},
// 最大值
max: {
type: [String, Number],
default: Number.MAX_SAFE_INTEGER
},
// 加减的步长,可为小数
step: {
type: [String, Number],
default: 1
},
// 是否只允许输入整数
integer: {
type: Boolean,
default: true
},
// 是否禁用,包括输入框,加减按钮
disabled: {
type: Boolean,
default: false
},
// 是否禁用输入框
disabledInput: {
type: Boolean,
default: false
},
// 是否开启异步变更,开启后需要手动控制输入值
asyncChange: {
type: Boolean,
default: false
},
// 输入框宽度单位为px
inputWidth: {
type: [String, Number],
default: '80rpx'
},
// 是否显示减少按钮
showMinus: {
type: Boolean,
default: true
},
// 是否显示增加按钮
showPlus: {
type: Boolean,
default: true
},
// 显示的小数位数
decimalLength: {
type: [String, Number, null],
default: null
},
// 是否开启长按加减手势
longPress: {
type: Boolean,
default: true
},
// 输入框文字和加减按钮图标的颜色
color: {
type: String,
default: '#331353D'
},
// 按钮大小宽高等于此值单位px输入框高度和此值保持一致
buttonSize: {
type: [String, Number],
default: '64rpx'
},
// 输入框和按钮的背景颜色
bgColor: {
type: String,
default: '#EBECEE'
},
// 指定光标于键盘的距离避免键盘遮挡输入框单位px
cursorSpacing: {
type: [String, Number],
default: 100
},
// 是否禁用增加按钮
disablePlus: {
type: Boolean,
default: false
},
// 是否禁用减少按钮
disableMinus: {
type: Boolean,
default: false
},
// 加减按钮图标的样式
iconStyle: {
type: [Object, String],
default: ''
}
}
}

View File

@@ -0,0 +1,225 @@
<template>
<view class="previewImage" :style="{ 'background-color':'rgba(0,0,0,'+opacity+')'}" v-if="show" @tap="close" @touchmove.stop.prevent >
<swiper class="swiper" :current="index" @change="change">
<swiper-item v-for="(img, i) in urls" :key="i" >
<movable-area class="marea" scale-area>
<movable-view class="mview" direction="all" :out-of-bounds="false" scale="true" @scale="onScale" :inertia="true" damping="90" friction="0.9" scale-min="0.9" scale-max="4" :scale-value="scale">
<image class="image" :src="img" :data-index="i" :data-src="img" mode="widthFix" @touchmove="handletouchmove" @touchstart="handletouchstart" @touchend="handletouchend" />
</movable-view>
</movable-area>
</swiper-item>
</swiper>
<view class="page" v-if="urls.length > 0">
<text class="text">{{ index+1 }} / {{ urls.length }}</text>
</view>
<view class="desc" v-if="descs.length > 0 && descs.length == urls.length&&descs[index].length>0">{{ descs[index] }}</view>
<view class="left-pre" v-if="index > 0" @tap.native.stop='goPre'>
<u-icon name="arrow-right" size="64rpx" color="#fff"></u-icon>>
</view>
<view class="right-next" v-if="urls.length-1-index > 0" @tap.native.stop="goNext">
<u-icon name="arrow-right" size="64rpx" color="#fff"></u-icon>>
</view>
</view>
</template>
<script>
export default {
name: 'd-previewImage', //插件名称
props: {
imgs: {//图片列表
type: Array,
required: true,
default: () => {
return [];
}
},
descs: {//描述列表
type: Array,
required: false,
default: () => {
return [];
}
},
//透明度,0到1之间。
opacity: {
type: Number,
default: 0.8
}
},
data() {
return {
show: false, //显示状态
index: 0, //当前页
time:0,//定时器
interval: 1000, //长按事件
scale: 1, //缩放比例
old: {
scale: 1 //缩放比例
},
urls:this.imgs
};
},
methods: {
goPre(){
this.index = this.index-1;
},
goNext(){
this.index = this.index+1;
},
onScale(e) {
this.old.scale = e.detail.scale
},
//接触开始
handletouchstart(e) {
var tchs = e.touches.length;
if (tchs != 1) {
return false;
}
this.time = setTimeout(() => {
this.onLongPress(e);
}, this.interval);
return false;
},
//清除定时器
handletouchend() {
clearTimeout(this.time);
if (this.time != 0) {
//处理点击时间
}
return false;
},
//清除定时器
handletouchmove() {
clearTimeout(this.time);
this.time = 0;
},
// 处理长按事件
onLongPress(e) {
var src = e.currentTarget.dataset.src;
var index = e.currentTarget.dataset.index;
var data={src:src,index:index}
this.$emit('longPress', data);
},
//图片改变
change(e) {
this.scale = 1;
this.index = e.target.current;
},
//打开
open(urls,e=0) {
if (e===null||e==="") {
return;
}
if(!isNaN(e)){
this.index = e;
}else{
this.index = this.urls.indexOf(e);
}
this.urls = urls
this.show = true;
},
//关闭
close(e) {
this.show = false;
}
}
};
</script>
<!--使用scss,只在本组件生效-->
<style lang="scss" scoped>
.previewImage {
z-index: 999;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #000000;
user-select: none;
.left-pre{
position: absolute;
left: 0;
top: calc(50% - 78rpx);
background: rgba(0, 0, 0, 0.4);
width: 78rpx;
height: 156rpx;
border-radius: 0 78rpx 78rpx 0;
z-index: 1001;
.icon-arrow{
width: 64rpx;
height: 64rpx;
margin-top: 46rpx;
}
}
.right-next{
position: absolute;
right: 0;
top: calc(50% - 78rpx);
width: 78rpx;
height: 156rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 78rpx 0 0 78rpx;
z-index: 1001;
.icon-arrow{
margin-top: 46rpx;
margin-left: 12rpx;
width: 64rpx;
height: 64rpx;
}
}
.swiper {
width: 100%;
height: 100%;
.marea {
height: 100%;
width: 100%;
position: fixed;
overflow: hidden;
.mview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: auto;
min-height: 100%;
.image {
width: 100%;
}
}
}
}
.page {
position: absolute;
width: 100%;
bottom: 60rpx;
text-align: center;
.text {
color: #fff;
font-size: 34rpx;
font-size: 500;
padding: 3rpx 16rpx;
border-radius: 20rpx;
}
}
.desc {
position: absolute;
top: 51rpx;
width: 100%;
padding: 5rpx 10rpx;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
// background-color: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 46rpx;
font-size: 500;
letter-spacing: 3rpx;
}
}
</style>

View File

@@ -0,0 +1,191 @@
<template>
<view class="">
<view class="u-steps" :style="{
flexDirection: direction
}">
<view class="u-steps__item" :class="['u-steps__item--' + direction]" v-for="(item, index) in list"
:key="index">
<view class="u-steps__item__num" v-if="mode == 'number'" :style="{
backgroundColor: current < index ? unActiveColor : activeColor
}">
<u-icon size="20" color="#ffffff" :name="icon"></u-icon>
</view>
<view class="u-steps__item__dot" v-if="mode == 'dot'" :style="{
backgroundColor: index <= current ? activeColor : unActiveColor
}"></view>
<text class="u-line-1" :style="{
color: index <= current ? activeColor : unActiveColor,
}" :class="['u-steps__item__text--' + direction]">
{{ item.name }}
</text>
<view class="u-steps__item__line" :style="{width:`calc(calc(100vw - 216rpx - ${(list.length-2)*48}rpx) / ${list.length-1})`}" :class="['u-steps__item__line--' + mode]"
v-if="index < list.length - 1">
<u-line :direction="direction" length="100%" :hair-line="false"
:color="index <= current ? activeColor : unActiveColor"></u-line>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* steps 步骤条
* @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
* @tutorial https://www.uviewui.com/components/steps.html
* @property {String} mode 设置模式默认dot
* @property {Array} list 数轴条数据,数组。具体见上方示例
* @property {String} type type主题默认primary
* @property {String} direction row-横向column-竖向默认row
* @property {Number String} current 设置当前处于第几步
* @property {String} active-color 已完成步骤的激活颜色如设置type值会失效
* @property {String} un-active-color 未激活的颜色,用于表示未完成步骤的颜色(默认#606266
* @example <u-steps :list="numList" active-color="#fa3534"></u-steps>
*/
export default {
name: 'u-steps',
props: {
// 步骤条的类型dot|number
mode: {
type: String,
default: 'dot'
},
// 步骤条的数据
list: {
type: Array,
default () {
return [];
}
},
// 主题类型, primary|success|info|warning|error
type: {
type: String,
default: 'primary'
},
// 当前哪一步是激活的
current: {
type: [Number, String],
default: 0
},
// 激活步骤的颜色
activeColor: {
type: String,
default: '#6ACDBB'
},
// 未激活的颜色
unActiveColor: {
type: String,
default: '#C4C7CC'
},
// 自定义图标
icon: {
type: String,
default: 'checkmark'
},
// step的排列方向row-横向column-竖向
direction: {
type: String,
default: 'row'
}
},
data() {
return {};
},
};
</script>
<style lang="scss" scoped>
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
$u-steps-item-number-width: 32rpx;
$u-steps-item-dot-width: 20rpx;
.u-steps {
@include vue-flex;
padding: 0 32rpx;
justify-content: space-between;
.u-steps__item {
text-align: center;
position: relative;
width: 96rpx;
color: #C4C7CC;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
&--row {
position: relative;
@include vue-flex;
flex-direction: column;
.u-steps__item__line {
position: absolute;
z-index: 0;
left: 77%;
&--dot {
top: calc(#{$u-steps-item-dot-width} / 2);
}
&--number {
top: calc(#{$u-steps-item-number-width} / 2);
}
}
}
&--column {
@include vue-flex;
flex-direction: row;
justify-content: flex-start;
min-height: 120rpx;
.u-steps__item__line {
position: absolute;
z-index: 0;
height: 50%;
top: 40vw;
&--dot {
left: calc(#{$u-steps-item-dot-width} / 2);
}
&--number {
left: calc(#{$u-steps-item-number-width} / 2);
}
}
}
&__num {
@include vue-flex;
align-items: center;
justify-content: center;
width: $u-steps-item-number-width;
height: $u-steps-item-number-width;
border-radius: 50%;
overflow: hidden;
}
&__dot {
width: $u-steps-item-dot-width;
height: $u-steps-item-dot-width;
@include vue-flex;
border-radius: $u-steps-item-dot-width;
}
&__text--row {
margin-top: 14rpx;
}
&__text--column {
margin-left: 14rpx;
}
}
}
</style>

View File

@@ -0,0 +1,381 @@
<template>
<view v-if="show" class="u-tabbar" @touchmove.stop.prevent="() => {}">
<view class="u-tabbar__content safe-area-inset-bottom" :style="{
height: $u.addUnit(height),
backgroundColor: bgColor,
}" :class="{
'u-border-top': borderTop
}">
<block v-for="(item, index) in list" :key="index">
<view class="u-tabbar__content__item" v-if="item.permissions.includes(parseInt(identity)-1)" :class="{
'u-tabbar__content__circle': midButton &&item.midButton
}" @tap.stop="clickHandler(index)" :style="{
backgroundColor: bgColor
}">
<view :class="[
midButton && item.midButton ? 'u-tabbar__content__circle__button' : 'u-tabbar__content__item__button'
]">
<image :style="{width: iconSize,height: iconSize}" :src="elIconPath(index)" mode="scaleToFill">
</image>
<u-badge :count="item.count" :is-dot="item.isDot" v-if="item.count || item.isDot"
:offset="[-2, getOffsetRight(item.count, item.isDot)]"></u-badge>
</view>
<view class="u-tabbar__content__item__text" :style="{
color: elColor(index)
}">
<text class="u-line-1">{{item.text}}</text>
</view>
</view>
</block>
<view v-if="midButton" class="u-tabbar__content__circle__border" :class="{
'u-border': borderTop,
}" :style="{
backgroundColor: bgColor,
left: midButtonLeft
}">
</view>
</view>
<!-- 这里加上一个48rpx的高度,是为了增高有凸起按钮时的防塌陷高度(也即按钮凸出来部分的高度) -->
<view class="u-fixed-placeholder safe-area-inset-bottom" :style="{
height: `calc(${$u.addUnit(height)} + ${midButton ? 48 : 0}rpx)`,
}"></view>
</view>
</template>
<script>
export default {
props: {
// 显示与否
show: {
type: Boolean,
default: true
},
// 通过v-model绑定current值
value: {
type: [String, Number],
default: 0
},
// 整个tabbar的背景颜色
bgColor: {
type: String,
default: '#fff'
},
// tabbar的高度默认50px单位任意如果为数值则为rpx单位
height: {
type: [String, Number],
default: '104rpx'
},
// 非凸起图标的大小单位任意数值默认rpx
iconSize: {
type: [String, Number],
default: '44rpx'
},
// 凸起的图标的大小单位任意数值默认rpx
midButtonSize: {
type: [String, Number],
default: 90
},
// 激活时的演示,包括字体图标,提示文字等的演示
activeColor: {
type: String,
default: '#6ACDBB'
},
// 未激活时的颜色
inactiveColor: {
type: String,
default: '#6C7380'
},
// 是否显示中部的凸起按钮
midButton: {
type: Boolean,
default: false
},
// 切换前的回调
beforeSwitch: {
type: Function,
default: null
},
// 是否显示顶部的横线
borderTop: {
type: Boolean,
default: true
},
// 是否隐藏原生tabbar
hideTabBar: {
type: Boolean,
default: true
},
},
data() {
return {
// 配置参数
list: [{
iconPath: require("../../static/image/w-gzt.png"),
selectedIconPath: require("../../static/image/gzt.png"),
text: '工作台',
path: 'pages/workbench/index',
permissions: [0]
},
{
iconPath: require("../../static/image/w-hz.png"),
selectedIconPath: require("../../static/image/hz.png"),
text: '患者',
path: 'pages/patient/index',
permissions: [0]
},
// {
// iconPath: require("../../static/image/w-wdwz.png"),
// selectedIconPath: require("../../static/image/wdwz.png"),
// text: '文章中心',
// path: 'pages/article/index',
// permissions: [0]
// },
// {
// iconPath: require("../../static/image/w-sf.png"),
// selectedIconPath: require("../../static/image/sf1.png"),
// text: '审方',
// path: 'pages/pharmacist/index',
// permissions: [2]
// },
{
iconPath: require("../../static/image/w-wd.png"),
selectedIconPath: require("../../static/image/wd.png"),
text: '我的',
path: 'pages/my/index',
permissions: [0]
},
// {
// iconPath: require("../../static/image/w-huihua.png"),
// selectedIconPath: require("../../static/image/huiua.png"),
// text: '会话',
// path: 'pages/pharmacist/index',
// permissions: [2, 3]
// },
// {
// iconPath: require("../../static/image/w-sz.png"),
// selectedIconPath: require("../../static/image/sz.png"),
// text: '设置',
// path: 'pages/my/index',
// permissions: [2, 3]
// }
],
// 由于安卓太菜了通过css居中凸起按钮的外层元素有误差故通过js计算将其居中
midButtonLeft: '50%',
pageUrl: '', // 当前页面URL
// 身份
identity: uni.getStorageSync('role') || 1,
}
},
created() {
// 是否隐藏原生tabbar
if (this.hideTabBar) uni.hideTabBar();
// 获取引入了u-tabbar页面的路由地址该地址没有路径前面的"/"
let pages = getCurrentPages();
// 页面栈中的最后一个即为项为当前页面route属性为页面路径
this.pageUrl = pages[pages.length - 1].route;
},
computed: {
elIconPath() {
return (index) => {
// 历遍u-tabbar的每一项item时判断是否传入了pagePath参数如果传入了
// 和data中的pageUrl参数对比如果相等即可判断当前的item对应当前的tabbar页面设置高亮图标
// 采用这个方法可以无需使用v-model绑定的value值
let pagePath = this.list[index].path;
// 如果定义了pagePath属性意味着使用系统自带tabbar方案否则使用一个页面用几个组件模拟tabbar页面的方案
// 这两个方案对处理tabbar item的激活与否方式不一样
if (pagePath) {
if (pagePath == this.pageUrl || pagePath == '/' + this.pageUrl) {
return this.list[index].selectedIconPath;
} else {
return this.list[index].iconPath;
}
} else {
// 普通方案中索引等于v-model值时即为激活项
return index == this.value ? this.list[index].selectedIconPath : this.list[index].iconPath
}
}
},
elColor() {
return (index) => {
// 判断方法同理于elIconPath
let pagePath = this.list[index].path;
if (pagePath) {
if (pagePath == this.pageUrl || pagePath == '/' + this.pageUrl) return this.activeColor;
else return this.inactiveColor;
} else {
return index == this.value ? this.activeColor : this.inactiveColor;
}
}
}
},
mounted() {
this.midButton && this.getMidButtonLeft();
},
methods: {
async clickHandler(index) {
if (this.beforeSwitch && typeof(this.beforeSwitch) === 'function') {
// 执行回调,同时传入索引当作参数
// 在微信,支付宝等环境(H5正常)会导致父组件定义的customBack()函数体中的this变成子组件的this
// 通过bind()方法绑定父组件的this让this.customBack()的this为父组件的上下文
let beforeSwitch = this.beforeSwitch.bind(this.$u.$parent.call(this))(index);
// 判断是否返回了promise
if (!!beforeSwitch && typeof beforeSwitch.then === 'function') {
await beforeSwitch.then(res => {
// promise返回成功
this.switchTab(index);
}).catch(err => {
})
} else if (beforeSwitch === true) {
// 如果返回true
this.switchTab(index);
}
} else {
this.switchTab(index);
}
},
// 切换tab
switchTab(index) {
// 发出事件和修改v-model绑定的值
this.$emit('change', index);
// 如果有配置path属性使用uni.switchTab进行跳转
if (this.list[index].path) {
uni.switchTab({
url: '../../' + this.list[index].path,
fail: (e) => {
console.log(e);
}
})
} else {
// 如果配置了papgePath属性将不会双向绑定v-model传入的value值
// 因为这个模式下不再需要v-model绑定的value值了而是通过getCurrentPages()适配
this.$emit('input', index);
}
},
// 计算角标的right值
getOffsetRight(count, isDot) {
// 点类型count大于9(两位数)分别设置不同的right值避免位置太挤
if (isDot) {
return -20;
} else if (count > 9) {
return -40;
} else {
return -30;
}
},
// 获取凸起按钮外层元素的left值让其水平居中
getMidButtonLeft() {
let windowWidth = this.$u.sys().windowWidth;
// 由于安卓中css计算left: 50%的结果不准确故用js计算
this.midButtonLeft = (windowWidth / 2) + 'px';
}
}
}
</script>
<style scoped lang="scss">
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.u-fixed-placeholder {
/* #ifndef APP-NVUE */
box-sizing: content-box;
/* #endif */
}
.u-tabbar {
&__content {
@include vue-flex;
align-items: center;
position: relative;
position: fixed;
bottom: 0;
left: 0;
width: 100%;
z-index: 998;
/* #ifndef APP-NVUE */
box-sizing: content-box;
/* #endif */
border-top: 2rpx solid #F2F3F5;
&__circle__border {
border-radius: 100%;
width: 110rpx;
height: 110rpx;
top: -48rpx;
position: absolute;
z-index: 4;
background-color: #ffffff;
// 由于安卓的无能导致只有3个tabbar item时此css计算方式有误差
// 故使用js计算的形式来定位此处不注释是因为js计算有延后避免出现位置闪动
left: 50%;
transform: translateX(-50%);
&:after {
border-radius: 100px;
}
}
&__item {
flex: 1;
justify-content: center;
height: 100%;
@include vue-flex;
flex-direction: column;
align-items: center;
position: relative;
&__button {
position: absolute;
top: 12rpx;
left: 50%;
transform: translateX(-50%);
}
&__text {
font-size: 20rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #2979FF;
position: absolute;
top: 68rpx;
left: 50%;
transform: translateX(-50%);
width: 100%;
text-align: center;
}
}
&__circle {
position: relative;
@include vue-flex;
flex-direction: column;
justify-content: space-between;
z-index: 10;
/* #ifndef APP-NVUE */
height: calc(100% - 1px);
/* #endif */
&__button {
width: 90rpx;
height: 90rpx;
border-radius: 100%;
@include vue-flex;
justify-content: center;
align-items: center;
position: absolute;
background-color: #ffffff;
top: -40rpx;
left: 50%;
z-index: 6;
transform: translateX(-50%);
}
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view class="u-text" :class="[{'text-twoline':lines>0},className]" :style="[valueStyle]" @tap="clickHandler">
{{text}}
</view>
</template>
<script>
import value from './value.js';
import props from './props.js';
/**
* Text 文本
* @description 文本
* @tutorial https://www.uviewui.com/components/loading.html
* @property {String | Number} text 显示的值
* @property {Boolean} bold 是否粗体默认normal默认 false
* @property {Boolean} block 是否块状(默认 false
* @property {String | Number} lines 文本显示的行数,如果设置,超出此行数,将会显示省略号
* @property {String} color 文本颜色(默认 '#A7ABB0'
* @property {String | Number} size 字体大小(默认 28rpx
* @property {String | Number} lineHeight 文本行高
* @property {String} align 文本对齐方式可选值left|center|right|justify|start|end默认 'left'
* @property {String} wordWrap 文字换行可选值break-word|normal|anywhere默认 'normal'
* @property {String} decoration 文字装饰下划线中划线等可选值none|underline|line-through默认 'none'
* @property {String} className 接收自定义类名
* @property {String} width 宽度(默认没有)
* @event {Function} click 点击触发事件
* @example <d-text text="我用十年青春,赴你最后之约"></d-text>
*/
export default {
name: 'd-text',
mixins: [value, props],
computed: {
valueStyle() {
const style = {
textDecoration: this.decoration,
fontWeight: this.bold ? 'bold' : 'normal',
textAlignLast: this.align,
wordWrap: this.wordWrap,
fontSize: uni.$u.addUnit(this.size)
};
this.width && (style.width = uni.$u.addUnit(this.width))
style.color = this.color;
this.lines && (style['-webkit-line-clamp'] = this.lines);
this.lineHeight && (style.lineHeight = uni.$u.addUnit(this.lineHeight));
this.block && (style.display = 'block');
return uni.$u.deepMerge(style, uni.$d.addStyle(this.customStyle));
},
isMp() {
let mp = false;
// #ifdef MP
mp = true;
// #endif
return mp;
}
},
data() {
return {};
},
onShow() {},
methods: {
addStyle(customStyle) {
uni.$d.addStyle(customStyle)
},
clickHandler() {
// 如果为手机号模式,拨打电话
if (this.mode === 'phone' && uni.$u.test.mobile(this.text)) {
uni.makePhoneCall({
phoneNumber: this.text
});
}
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
.text-twoline {
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
</style>

View File

@@ -0,0 +1,67 @@
export default {
props: {
// 显示的值
text: {
type: String,
default: ''
},
// 是否粗体默认normal
bold: {
type: Boolean,
default: false
},
// 是否块状
block: {
type: Boolean,
default: false
},
// 文本显示的行数,如果设置,超出此行数,将会显示省略号
lines: {
type: [String, Number],
default: ''
},
// 文本颜色
color: {
type: String,
default: '#A7ABB0'
},
// 字体大小
size: {
type: [String, Number],
default: '28rpx'
},
// 文字装饰,下划线,中划线等,可选值 none|underline|line-through
decoration: {
tepe: String,
default: 'none',
},
// 外边距,对象、字符串,数值形式均可
margin: {
type: [Object, String, Number],
default: 0
},
// 文本行高
lineHeight: {
type: [String, Number],
default: ''
},
// 文本对齐方式可选值left|center|right
align: {
type: String,
default: 'left'
},
// 文字换行可选值break-word|normal|anywhere
wordWrap: {
type: String,
default: 'normal'
},
className: {
type: String,
default: ''
},
width: {
type: [String,Number],
default: ''
}
}
}

View File

@@ -0,0 +1,85 @@
export default {
computed: {
// 经处理后需要显示的值
value() {
const {
text,
mode,
format,
href
} = this
// 价格类型
if (mode === 'price') {
// 如果text不为金额进行提示
if (!/^\d+(\.\d+)?$/.test(text)) {
uni.$u.error('金额模式下text参数需要为金额格式');
}
// 进行格式化判断用户传入的format参数为正则或者函数如果没有传入format则使用默认的金额格式化处理
if (uni.$u.test.func(format)) {
// 如果用户传入的是函数,使用函数格式化
return format(text)
}
// 如果format非正则非函数则使用默认的金额格式化方法进行操作
return uni.$u.priceFormat(text, 2)
} if (mode === 'date') {
// 判断是否合法的日期或者时间戳
!uni.$u.test.date(text) && uni.$u.error('日期模式下text参数需要为日期或时间戳格式')
// 进行格式化判断用户传入的format参数为正则或者函数如果没有传入format则使用默认的格式化处理
if (uni.$u.test.func(format)) {
// 如果用户传入的是函数,使用函数格式化
return format(text)
} if (this.formart) {
// 如果format非正则非函数则使用默认的时间格式化方法进行操作
return uni.$u.timeFormat(text, format)
}
// 如果没有设置format则设置为默认的时间格式化形式
return uni.$u.timeFormat(text, 'yyyy-mm-dd')
} if (mode === 'phone') {
// 判断是否合法的手机号
!uni.$u.test.mobile(text) && uni.$u.error('手机号模式下text参数需要为手机号码格式')
if (uni.$u.test.func(format)) {
// 如果用户传入的是函数,使用函数格式化
return format(text)
} if (format === 'encrypt') {
// 如果format为encrypt则将手机号进行星号加密处理
return `${text.substr(0, 3)}****${text.substr(7)}`
}
return text
} if (mode === 'name') {
// 判断是否合法的字符粗
!(typeof (text) === 'string') && uni.$u.error('姓名模式下text参数需要为字符串格式')
if (uni.$u.test.func(format)) {
// 如果用户传入的是函数,使用函数格式化
return format(text)
} if (format === 'encrypt') {
// 如果format为encrypt则将姓名进行星号加密处理
return this.formatName(text)
}
return text
} if (mode === 'link') {
// 判断是否合法的字符粗
!uni.$u.test.url(href) && uni.$u.error('超链接模式下href参数需要为URL格式')
return text
}
return text
}
},
methods: {
// 默认的姓名脱敏规则
formatName(name) {
let value = ''
if (name.length === 2) {
value = name.substr(0, 1) + '*'
} else if (name.length > 2) {
let char = ''
for (let i = 0, len = name.length - 2; i < len; i++) {
char += '*'
}
value = name.substr(0, 1) + char + name.substr(-1, 1)
} else {
value = name
}
return value
}
}
}

View File

@@ -0,0 +1,114 @@
<template>
<view
v-if="inited"
class="u-transition"
ref="u-transition"
@tap="clickHandler"
:class="classes"
:style="[mergeStyle]"
@touchmove="noop"
>
<slot />
</view>
</template>
<script>
import props from './props.js';
// 组件的methods方法由于内容较长写在外部文件中通过mixin引入
import transition from "./transition.js";
/**
* transition 动画组件
* @description
* @tutorial
* @property {String} show 是否展示组件 (默认 false
* @property {String} mode 使用的动画模式 (默认 'fade'
* @property {String | Number} duration 动画的执行时间单位ms (默认 '300'
* @property {String} timingFunction 使用的动画过渡函数 (默认 'ease-out'
* @property {Object} customStyle 自定义样式
* @event {Function} before-enter 进入前触发
* @event {Function} enter 进入中触发
* @event {Function} after-enter 进入后触发
* @event {Function} before-leave 离开前触发
* @event {Function} leave 离开中触发
* @event {Function} after-leave 离开后触发
* @example
*/
export default {
name: 'd-transition',
data() {
return {
inited: false, // 是否显示/隐藏组件
viewStyle: {}, // 组件内部的样式
status: '', // 记录组件动画的状态
transitionEnded: false, // 组件是否结束的标记
display: false, // 组件是否展示
classes: '', // 应用的类名
}
},
computed: {
mergeStyle() {
const { viewStyle, customStyle } = this
return {
// #ifndef APP-NVUE
transitionDuration: `${this.duration}ms`,
// display: `${this.display ? '' : 'none'}`,
transitionTimingFunction: this.timingFunction,
// #endif
// 避免自定义样式影响到动画属性所以写在viewStyle前面
...uni.$d.addStyle(customStyle),
...viewStyle
}
}
},
// 将mixin挂在到组件中uni.$u.mixin实际上为一个vue格式对象
mixins: [transition, props],
watch: {
show: {
handler(newVal) {
// vue和nvue分别执行不同的方法
// #ifdef APP-NVUE
newVal ? this.nvueEnter() : this.nvueLeave()
// #endif
// #ifndef APP-NVUE
newVal ? this.vueEnter() : this.vueLeave()
// #endif
},
// 表示同时监听初始化时的props的show的意思
immediate: true
}
}
}
</script>
<style lang="scss" scoped>
@mixin flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: $direction;
}
/* #ifndef APP-NVUE */
// 由于uView是基于nvue环境进行开发的此环境中普通元素默认为flex-direction: column;
// 所以在非nvue中需要对元素进行重置为flex-direction: column; 否则可能会表现异常
view,
scroll-view,
swiper-item {
display: flex;
flex-direction: column;
flex-shrink: 0;
flex-grow: 0;
flex-basis: auto;
align-items: stretch;
align-content: flex-start;
}
/* #endif */
/* #ifndef APP-NVUE */
// vue版本动画相关的样式抽离在外部文件
@import './vue.ani-style.scss';
/* #endif */
.u-transition {}
</style>

View File

@@ -0,0 +1,68 @@
export default {
fade: {
enter: { opacity: 0 },
'enter-to': { opacity: 1 },
leave: { opacity: 1 },
'leave-to': { opacity: 0 }
},
'fade-up': {
enter: { opacity: 0, transform: 'translateY(100%)' },
'enter-to': { opacity: 1, transform: 'translateY(0)' },
leave: { opacity: 1, transform: 'translateY(0)' },
'leave-to': { opacity: 0, transform: 'translateY(100%)' }
},
'fade-down': {
enter: { opacity: 0, transform: 'translateY(-100%)' },
'enter-to': { opacity: 1, transform: 'translateY(0)' },
leave: { opacity: 1, transform: 'translateY(0)' },
'leave-to': { opacity: 0, transform: 'translateY(-100%)' }
},
'fade-left': {
enter: { opacity: 0, transform: 'translateX(-100%)' },
'enter-to': { opacity: 1, transform: 'translateY(0)' },
leave: { opacity: 1, transform: 'translateY(0)' },
'leave-to': { opacity: 0, transform: 'translateX(-100%)' }
},
'fade-right': {
enter: { opacity: 0, transform: 'translateX(100%)' },
'enter-to': { opacity: 1, transform: 'translateY(0)' },
leave: { opacity: 1, transform: 'translateY(0)' },
'leave-to': { opacity: 0, transform: 'translateX(100%)' }
},
'slide-up': {
enter: { transform: 'translateY(100%)' },
'enter-to': { transform: 'translateY(0)' },
leave: { transform: 'translateY(0)' },
'leave-to': { transform: 'translateY(100%)' }
},
'slide-down': {
enter: { transform: 'translateY(-100%)' },
'enter-to': { transform: 'translateY(0)' },
leave: { transform: 'translateY(0)' },
'leave-to': { transform: 'translateY(-100%)' }
},
'slide-left': {
enter: { transform: 'translateX(-100%)' },
'enter-to': { transform: 'translateY(0)' },
leave: { transform: 'translateY(0)' },
'leave-to': { transform: 'translateX(-100%)' }
},
'slide-right': {
enter: { transform: 'translateX(100%)' },
'enter-to': { transform: 'translateY(0)' },
leave: { transform: 'translateY(0)' },
'leave-to': { transform: 'translateX(100%)' }
},
zoom: {
enter: { transform: 'scale(0.95)' },
'enter-to': { transform: 'scale(1)' },
leave: { transform: 'scale(1)' },
'leave-to': { transform: 'scale(0.95)' }
},
'fade-zoom': {
enter: { opacity: 0, transform: 'scale(0.95)' },
'enter-to': { opacity: 1, transform: 'scale(1)' },
leave: { opacity: 1, transform: 'scale(1)' },
'leave-to': { opacity: 0, transform: 'scale(0.95)' }
}
}

View File

@@ -0,0 +1,30 @@
export default {
props: {
customStyle:{
type:Object,
default:()=>{
return {}
}
},
// 是否展示组件
show: {
type: Boolean,
default: false
},
// 使用的动画模式
mode: {
type: String,
default: 'fade'
},
// 动画的执行时间单位ms
duration: {
type: [String, Number],
default: '300'
},
// 使用的动画过渡函数
timingFunction: {
type: String,
default: 'ease-out'
}
}
}

View File

@@ -0,0 +1,155 @@
// 定义一个一定时间后自动成功的promise让调用nextTick方法处进入下一个then方法
const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 50))
// nvue动画模块实现细节抽离在外部文件
import animationMap from './nvue.ani-map.js'
// #ifndef APP-NVUE
// 定义类名通过给元素动态切换类名赋予元素一定的css动画样式
const getClassNames = (name) => ({
enter: `u-${name}-enter u-${name}-enter-active`,
'enter-to': `u-${name}-enter-to u-${name}-enter-active`,
leave: `u-${name}-leave u-${name}-leave-active`,
'leave-to': `u-${name}-leave-to u-${name}-leave-active`
})
// #endif
// #ifdef APP-NVUE
// 引入nvue(weex)的animation动画模块文档见
// https://weex.apache.org/zh/docs/modules/animation.html#transition
const animation = uni.requireNativePlugin('animation')
const getStyle = (name) => animationMap[name]
// #endif
export default {
methods: {
// 组件被点击发出事件
clickHandler() {
this.$emit('click')
},
// #ifndef APP-NVUE
// vue版本的组件进场处理
vueEnter() {
// 动画进入时的类名
const classNames = getClassNames(this.mode)
// 定义状态和发出动画进入前事件
this.status = 'enter'
this.$emit('beforeEnter')
this.inited = true
this.display = true
this.classes = classNames.enter
this.$nextTick(async () => {
// #ifdef H5
await uni.$u.sleep(20)
// #endif
// 组件动画进入后触发的事件
this.$emit('afterEnter')
// 标识动画尚未结束
this.transitionEnded = false
// 赋予组件enter-to类名
this.classes = classNames['enter-to']
})
},
// 动画离场处理
vueLeave() {
// 如果不是展示状态,无需执行逻辑
if (!this.display) return
const classNames = getClassNames(this.mode)
// 标记离开状态和发出事件
this.status = 'leave'
this.$emit('beforeLeave')
// 获得类名
this.classes = classNames.leave
this.$nextTick(() => {
// 标记动画已经结束了
this.transitionEnded = false
// 组件执行动画,到了执行的执行时间后,执行一些额外处理
setTimeout(this.onTransitionEnd, this.duration)
this.classes = classNames['leave-to']
})
},
// #endif
// #ifdef APP-NVUE
// nvue版本动画进场
nvueEnter() {
// 获得样式的名称
const currentStyle = getStyle(this.mode)
// 组件动画状态和发出事件
this.status = 'enter'
this.$emit('beforeEnter')
// 展示生成组件元素
this.inited = true
this.display = true
// 在nvue安卓上由于渲染速度慢在弹窗键盘日历等组件中渲染其中的内容需要时间
// 导致出现弹窗卡顿,这里让其一开始为透明状态,等一定时间渲染完成后,再让其隐藏起来,再让其按正常逻辑出现
this.viewStyle = {
opacity: 0
}
// 等待弹窗内容渲染完成
this.$nextTick(() => {
// 合并样式
this.viewStyle = currentStyle.enter
Promise.resolve()
.then(nextTick)
.then(() => {
// 组件开始进入前的事件
this.$emit('enter')
// nvue的transition动画模块需要通过ref调用组件注意此处的ref不同于vue的this.$refs['u-transition']用法
animation.transition(this.$refs['u-transition'].ref, {
styles: currentStyle['enter-to'],
duration: this.duration,
timingFunction: this.timingFunction,
needLayout: false,
delay: 0
}, () => {
// 动画执行完毕,发出事件
this.$emit('afterEnter')
})
})
.catch(() => {})
})
},
nvueLeave() {
if (!this.display) {
return
}
const currentStyle = getStyle(this.mode)
// 定义状态和事件
this.status = 'leave'
this.$emit('beforeLeave')
// 合并样式
this.viewStyle = currentStyle.leave
// 放到promise中处理执行过程
Promise.resolve()
.then(nextTick) // 等待几十ms
.then(() => {
this.transitionEnded = false
// 动画正在离场的状态
this.$emit('leave')
animation.transition(this.$refs['u-transition'].ref, {
styles: currentStyle['leave-to'],
duration: this.duration,
timingFunction: this.timingFunction,
needLayout: false,
delay: 0
}, () => {
this.onTransitionEnd()
})
})
.catch(() => {})
},
// #endif
// 完成过渡后触发
onTransitionEnd() {
// 如果已经是结束的状态,无需再处理
if (this.transitionEnded) return
this.transitionEnded = true
// 发出组件动画执行后的事件
this.$emit(this.status === 'leave' ? 'afterLeave' : 'afterEnter')
if (!this.show && this.display) {
this.display = false
this.inited = false
}
}
}
}

View File

@@ -0,0 +1,113 @@
/**
* vue版本动画内置的动画模式有如下
* fade淡入
* zoom缩放
* fade-zoom缩放淡入
* fade-up上滑淡入
* fade-down下滑淡入
* fade-left左滑淡入
* fade-right右滑淡入
* slide-up上滑进入
* slide-down下滑进入
* slide-left左滑进入
* slide-right右滑进入
*/
$u-zoom-scale: scale(0.95);
.u-fade-enter-active,
.u-fade-leave-active {
transition-property: opacity;
}
.u-fade-enter,
.u-fade-leave-to {
opacity: 0
}
.u-fade-zoom-enter,
.u-fade-zoom-leave-to {
transform: $u-zoom-scale;
opacity: 0;
}
.u-fade-zoom-enter-active,
.u-fade-zoom-leave-active {
transition-property: transform, opacity;
}
.u-fade-down-enter-active,
.u-fade-down-leave-active,
.u-fade-left-enter-active,
.u-fade-left-leave-active,
.u-fade-right-enter-active,
.u-fade-right-leave-active,
.u-fade-up-enter-active,
.u-fade-up-leave-active {
transition-property: opacity, transform;
}
.u-fade-up-enter,
.u-fade-up-leave-to {
transform: translate3d(0, 100%, 0);
opacity: 0
}
.u-fade-down-enter,
.u-fade-down-leave-to {
transform: translate3d(0, -100%, 0);
opacity: 0
}
.u-fade-left-enter,
.u-fade-left-leave-to {
transform: translate3d(-100%, 0, 0);
opacity: 0
}
.u-fade-right-enter,
.u-fade-right-leave-to {
transform: translate3d(100%, 0, 0);
opacity: 0
}
.u-slide-down-enter-active,
.u-slide-down-leave-active,
.u-slide-left-enter-active,
.u-slide-left-leave-active,
.u-slide-right-enter-active,
.u-slide-right-leave-active,
.u-slide-up-enter-active,
.u-slide-up-leave-active {
transition-property: transform;
}
.u-slide-up-enter,
.u-slide-up-leave-to {
transform: translate3d(0, 100%, 0)
}
.u-slide-down-enter,
.u-slide-down-leave-to {
transform: translate3d(0, -100%, 0)
}
.u-slide-left-enter,
.u-slide-left-leave-to {
transform: translate3d(-100%, 0, 0)
}
.u-slide-right-enter,
.u-slide-right-leave-to {
transform: translate3d(100%, 0, 0)
}
.u-zoom-enter-active,
.u-zoom-leave-active {
transition-property: transform
}
.u-zoom-enter,
.u-zoom-leave-to {
transform: $u-zoom-scale
}

View File

@@ -0,0 +1,658 @@
<template>
<view class="u-upload" v-if="!disabled">
<view v-if="showUploadList" class="u-list-item u-preview-wrap" v-for="(item, index) in lists" :key="index"
:style="[{
width: $u.addUnit(width),
height: $u.addUnit(height)
},customStyle]">
<view v-if="deletable" class="u-delete-icon" @tap.stop="deleteItem(index)" :style="{
background: delBgColor
}">
<u-icon class="u-icon" :name="delIcon" size="20" :color="delColor"></u-icon>
</view>
<u-line-progress v-if="showProgress && item.progress > 0 && !item.error" :show-percent="false" height="16"
class="u-progress" :percent="item.progress" active-color='#2979ff'></u-line-progress>
<view @tap.stop="retry(index)" v-if="item.error" class="u-error-btn">点击重试</view>
<image @tap.stop="doPreviewImage(item.url || item.path, index)" class="u-preview-image" v-if="!item.isImage"
:src="item.url || item.path" :mode="imageMode"></image>
</view>
<slot name="file" :file="lists"></slot>
<view style="display: inline-block;" @tap="selectFile" v-if="maxCount > lists.length">
<slot name="addBtn"></slot>
<view v-if="!customBtn" class="u-list-item u-add-wrap" hover-class="u-add-wrap__hover" hover-stay-time="150"
:style="[{
width: $u.addUnit(width),
height: $u.addUnit(height)
},customStyle]">
<u-icon name="plus" class="u-add-btn" size="40"></u-icon>
<view class="u-add-tips">{{ uploadText }}</view>
</view>
</view>
</view>
</template>
<script>
/**
* upload 图片上传
* @description 该组件用于上传图片场景
* @tutorial https://www.uviewui.com/components/upload.html
* @property {String} action 服务器上传地址
* @property {String Number} max-count 最大选择图片的数量默认99
* @property {Boolean} custom-btn 如果需要自定义选择图片的按钮设置为true默认false
* @property {Boolean} show-progress 是否显示进度条默认true
* @property {Boolean} disabled 是否启用(显示/移仓)组件默认false
* @property {String} image-mode 预览图片等显示模式可选值为uni的image的mode属性值默认aspectFill
* @property {String} del-icon 右上角删除图标名称只能为uView内置图标
* @property {String} del-bg-color 右上角关闭按钮的背景颜色
* @property {String | Number} index 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
* @property {String} del-color 右上角关闭按钮图标的颜色
* @property {Object} header 上传携带的头信息,对象形式
* @property {Object} form-data 上传额外携带的参数
* @property {String} name 上传文件的字段名供后端获取使用默认file
* @property {Array<String>} size-type original 原图compressed 压缩图,默认二者都有(默认['original', 'compressed']
* @property {Array<String>} source-type 选择图片的来源album-从相册选图camera-使用相机,默认二者都有(默认['album', 'camera']
* @property {Boolean} preview-full-image 是否可以通过uni.previewImage预览已选择的图片默认true
* @property {Boolean} multiple 是否开启图片多选部分安卓机型不支持默认true
* @property {Boolean} deletable 是否显示删除图片的按钮默认true
* @property {String Number} max-size 选择单个文件的最大大小单位B(byte)默认不限制默认Number.MAX_VALUE
* @property {Array<Object>} file-list 默认显示的图片列表数组元素为对象必须提供url属性
* @property {Boolean} upload-text 选择图片按钮的提示文字(默认“选择图片”)
* @property {Boolean} auto-upload 选择完图片是否自动上传见上方说明默认true
* @property {Boolean} show-tips 特殊情况下是否自动提示toast见上方说明默认true
* @property {Boolean} show-upload-list 是否显示组件内部的图片预览默认true
* @event {Function} on-oversize 图片大小超出最大允许大小
* @event {Function} on-preview 全屏预览图片时触发
* @event {Function} on-remove 移除图片时触发
* @event {Function} on-success 图片上传成功时触发
* @event {Function} on-change 图片上传后,无论成功或者失败都会触发
* @event {Function} on-error 图片上传失败时触发
* @event {Function} on-progress 图片上传过程中的进度变化过程触发
* @event {Function} on-uploaded 所有图片上传完毕触发
* @event {Function} on-choose-complete 每次选择图片后触发,只是让外部可以得知每次选择后,内部的文件列表
* @example <u-upload :action="action" :file-list="fileList" ></u-upload>
*/
export default {
name: 'd-upload',
props: {
//是否显示组件自带的图片预览功能
showUploadList: {
type: Boolean,
default: true
},
// 后端地址
action: {
type: String,
default: 'https://app.xiaokang88.com/service/v1/attachment/upload'
},
// 最大上传数量
maxCount: {
type: [String, Number],
default: 52
},
// 是否显示进度条
showProgress: {
type: Boolean,
default: true
},
// 是否启用
disabled: {
type: Boolean,
default: false
},
// 预览上传的图片时的裁剪模式和image组件mode属性一致
imageMode: {
type: String,
default: 'aspectFill'
},
// 头部信息
header: {
type: Object,
default () {
return {
};
}
},
// 额外携带的参数
formData: {
type: Object,
default () {
return {
store_id: '11001'
};
}
},
// 上传的文件字段名
name: {
type: String,
default: 'file'
},
// 所选的图片的尺寸, 可选值为original compressed
sizeType: {
type: Array,
default () {
return ['original', 'compressed'];
}
},
sourceType: {
type: Array,
default () {
return ['album', 'camera'];
}
},
// 是否在点击预览图后展示全屏图片预览
previewFullImage: {
type: Boolean,
default: true
},
// 是否开启图片多选,部分安卓机型不支持
multiple: {
type: Boolean,
default: true
},
// 是否展示删除按钮
deletable: {
type: Boolean,
default: true
},
// 文件大小限制单位为byte
maxSize: {
type: [String, Number],
default: Number.MAX_VALUE
},
// 显示已上传的文件列表
fileList: {
type: Array,
default () {
return [];
}
},
// 上传区域的提示文字
uploadText: {
type: String,
default: '选择图片'
},
// 是否自动上传
autoUpload: {
type: Boolean,
default: true
},
// 是否显示toast消息提示
showTips: {
type: Boolean,
default: true
},
// 是否通过slot自定义传入选择图标的按钮
customBtn: {
type: Boolean,
default: false
},
// 内部预览图片区域和选择图片按钮的区域宽度
width: {
type: [String, Number],
default: 200
},
// 内部预览图片区域和选择图片按钮的区域高度
height: {
type: [String, Number],
default: 200
},
// 右上角关闭按钮的背景颜色
delBgColor: {
type: String,
default: '#fa3534'
},
// 右上角关闭按钮的叉号图标的颜色
delColor: {
type: String,
default: '#ffffff'
},
// 右上角删除图标名称只能为uView内置图标
delIcon: {
type: String,
default: 'close'
},
// 如果上传后的返回值为json字符串是否自动转json
toJson: {
type: Boolean,
default: true
},
// 上传前的钩子,每个文件上传前都会执行
beforeUpload: {
type: Function,
default: null
},
// 移除文件前的钩子
beforeRemove: {
type: Function,
default: null
},
// 允许上传的图片后缀
limitType: {
type: Array,
default () {
// 支付宝小程序真机选择图片的后缀为"image"
// https://opendocs.alipay.com/mini/api/media-image
return ['png', 'jpg', 'jpeg', 'webp', 'gif', 'image'];
}
},
// 在各个回调事件中的最后一个参数返回,用于区别是哪一个组件的事件
index: {
type: [Number, String],
default: ''
},
customStyle: {
type: Object,
default () {
return {};
}
}
},
mounted() {},
data() {
return {
lists: [],
isInCount: true,
uploading: false
};
},
watch: {
fileList: {
immediate: true,
handler(val) {
val.map(value => {
// 首先检查内部是否已经添加过这张图片因为外部绑定了一个对象给fileList的话(对象引用)进行修改外部fileList
// 时会触发watch导致重新把原来的图片再次添加到this.lists
// 数组的some方法意思是只要数组元素有任意一个元素条件符合就返回true而另一个数组的every方法的意思是数组所有元素都符合条件才返回true
let tmp = this.lists.some(val => {
return val.url == value.url;
})
// 如果内部没有这个图片(tmp为false),则添加到内部
value && !tmp && this.lists.push({
url: value.url,
error: false,
progress: 100
});
});
}
},
// 监听lists的变化发出事件
lists(n) {
this.$emit('on-list-change', n, this.index);
}
},
methods: {
// 清除列表
clear() {
this.lists = [];
},
// 重新上传队列中上传失败的所有文件
reUpload() {
this.uploadFile();
},
// 选择图片
selectFile() {
if (this.disabled) return;
const {
name = '', maxCount, multiple, maxSize, sizeType, lists, camera, compressed, maxDuration, sourceType
} = this;
let chooseFile = null;
const newMaxCount = maxCount - lists.length;
// 设置为只选择图片的时候使用 chooseImage 来实现
chooseFile = new Promise((resolve, reject) => {
uni.chooseImage({
count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1,
sourceType: sourceType,
sizeType,
success: resolve,
fail: reject
});
});
chooseFile
.then(res => {
let file = null;
let listOldLength = this.lists.length;
res.tempFiles.map((val, index) => {
// 检查文件后缀是否允许如果不在this.limitType内就会返回false
if (!this.checkFileExt(val)) return;
// 如果是非多选index大于等于1或者超出最大限制数量时不处理
if (!multiple && index >= 1) return;
if (val.size > maxSize) {
this.$emit('on-oversize', val, this.lists, this.index);
this.showToast('超出允许的文件大小');
} else {
if (maxCount <= lists.length) {
this.$emit('on-exceed', val, this.lists, this.index);
this.showToast('超出最大允许的文件个数');
return;
}
lists.push({
url: val.path,
progress: 0,
error: false,
file: val
});
}
});
// 每次图片选择完,抛出一个事件,并将当前内部选择的图片数组抛出去
this.$emit('on-choose-complete', this.lists, this.index);
if (this.autoUpload) this.uploadFile(listOldLength);
})
.catch(error => {
this.$emit('on-choose-fail', error);
});
},
// 提示用户消息
showToast(message, force = false) {
if (this.showTips || force) {
this.$toast(message)
}
},
// 该方法供用户通过ref调用手动上传
upload() {
this.uploadFile();
},
// 对失败的图片重新上传
retry(index) {
this.lists[index].progress = 0;
this.lists[index].error = false;
this.lists[index].response = null;
uni.showLoading({
title: '重新上传'
});
this.uploadFile(index);
},
// 上传图片
async uploadFile(index = 0) {
if (this.disabled) return;
if (this.uploading) return;
// 全部上传完成
if (index >= this.lists.length) {
this.$emit('on-uploaded', this.lists, this.index);
return;
}
// 检查是否是已上传或者正在上传中
if (this.lists[index].progress == 100) {
if (this.autoUpload == false) this.uploadFile(index + 1);
return;
}
// 执行before-upload钩子
if (this.beforeUpload && typeof(this.beforeUpload) === 'function') {
// 执行回调,同时传入索引和文件列表当作参数
// 在微信,支付宝等环境(H5正常)会导致父组件定义的customBack()函数体中的this变成子组件的this
// 通过bind()方法绑定父组件的this让this.customBack()的this为父组件的上下文
// 因为upload组件可能会被嵌套在其他组件内比如u-form这时this.$parent其实为u-form的this
// 非页面的this所以这里需要往上历遍一直寻找到最顶端的$parent这里用了this.$u.$parent.call(this)
// 明白意思即可无需纠结this.$u.$parent.call(this)的细节
let beforeResponse = this.beforeUpload.bind(this.$u.$parent.call(this))(index, this.lists);
// 判断是否返回了promise
if (!!beforeResponse && typeof beforeResponse.then === 'function') {
await beforeResponse.then(res => {
// promise返回成功不进行动作继续上传
}).catch(err => {
// 进入catch回调的话继续下一张
return this.uploadFile(index + 1);
})
} else if (beforeResponse === false) {
// 如果返回false继续下一张图片的上传
return this.uploadFile(index + 1);
} else {
// 此处为返回"true"的情形,这里不写代码,就跳过此处,继续执行当前的上传逻辑
}
}
// 检查上传地址
if (!this.action) {
this.showToast('请配置上传地址', true);
return;
}
this.lists[index].error = false;
this.uploading = true;
// 创建上传对象
const task = uni.uploadFile({
url: this.action,
filePath: this.lists[index].url,
name: this.name,
formData: this.formData,
header: {
...this.header,
authorization: `Bearer ${uni.getStorageSync('token')}`
},
success: res => {
// 判断是否json字符串将其转为json格式
let data = this.toJson && this.$u.test.jsonString(res.data) ? JSON.parse(res
.data) : res.data;
if (![200, 201, 204].includes(res.statusCode)) {
this.uploadError(index, data);
} else {
// 上传成功
this.lists[index].data = data;
this.lists[index].progress = 100;
this.lists[index].error = false;
this.lists[index].url = data.data.url;
this.$emit('on-success', data, index, this.lists, this.index);
}
},
fail: e => {
this.uploadError(index, e);
},
complete: res => {
uni.hideLoading();
this.uploading = false;
this.uploadFile(index + 1);
this.$emit('on-change', res, index, this.lists, this.index);
}
});
task.onProgressUpdate(res => {
if (res.progress > 0) {
this.lists[index].progress = res.progress;
this.$emit('on-progress', res, index, this.lists, this.index);
}
});
},
// 上传失败
uploadError(index, err) {
this.lists[index].progress = 0;
this.lists[index].error = true;
this.lists[index].response = null;
this.$emit('on-error', err, index, this.lists, this.index);
this.showToast('上传失败,请重试');
},
// 删除一个图片
deleteItem(index) {
uni.showModal({
title: '提示',
content: '您确定要删除此项吗?',
success: async (res) => {
if (res.confirm) {
// 先检查是否有定义before-remove移除前钩子
// 执行before-remove钩子
if (this.beforeRemove && typeof(this.beforeRemove) === 'function') {
// 此处钩子执行 原理同before-remove参数见上方注释
let beforeResponse = this.beforeRemove.bind(this.$u.$parent.call(this))(index,
this.lists);
// 判断是否返回了promise
if (!!beforeResponse && typeof beforeResponse.then === 'function') {
await beforeResponse.then(res => {
// promise返回成功不进行动作继续上传
this.handlerDeleteItem(index);
}).catch(err => {
// 如果进入promise的reject终止删除操作
this.showToast('已终止移除');
})
} else if (beforeResponse === false) {
// 返回false终止删除
this.showToast('已终止移除');
} else {
// 如果返回true执行删除操作
this.handlerDeleteItem(index);
}
} else {
// 如果不存在before-remove钩子
this.handlerDeleteItem(index);
}
}
}
});
},
// 执行移除图片的动作,上方代码只是判断是否可以移除
handlerDeleteItem(index) {
// 如果文件正在上传中终止上传任务进度在0 < progress < 100则意味着正在上传
if (this.lists[index].process < 100 && this.lists[index].process > 0) {
typeof this.lists[index].uploadTask != 'undefined' && this.lists[index].uploadTask.abort();
}
this.lists.splice(index, 1);
this.$forceUpdate();
this.$emit('on-remove', index, this.lists, this.index);
this.showToast('移除成功');
},
// 用户通过ref手动的形式移除一张图片
remove(index) {
// 判断索引的合法范围
if (index >= 0 && index < this.lists.length) {
this.lists.splice(index, 1);
this.$emit('on-list-change', this.lists, this.index);
}
},
// 预览图片
doPreviewImage(url, index) {
if (!this.previewFullImage) return;
const images = this.lists.map(item => item.url || item.path);
uni.previewImage({
urls: images,
current: url,
success: () => {
this.$emit('on-preview', url, this.lists, this.index);
},
fail: () => {
this.$toast('预览图片失败')
}
});
},
// 判断文件后缀是否允许
checkFileExt(file) {
// 检查是否在允许的后缀中
let noArrowExt = false;
// 获取后缀名
let fileExt = '';
const reg = /.+\./;
// 如果是H5需要从name中判断
// #ifdef H5
fileExt = file.name.replace(reg, "").toLowerCase();
// #endif
// 非H5需要从path中读取后缀
// #ifndef H5
fileExt = file.path.replace(reg, "").toLowerCase();
// #endif
// 使用数组的some方法只要符合limitType中的一个就返回true
noArrowExt = this.limitType.some(ext => {
// 转为小写
return ext.toLowerCase() === fileExt;
})
if (!noArrowExt) this.showToast(`不允许选择${fileExt}格式的文件`);
return noArrowExt;
}
}
};
</script>
<style lang="scss" scoped>
// 定义混入指令用于在非nvue环境下的flex定义因为nvue没有display属性会报错
@mixin vue-flex($direction: row) {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: $direction;
/* #endif */
}
.u-upload {
@include vue-flex;
flex-wrap: wrap;
align-items: center;
}
.u-list-item {
width: 200rpx;
height: 200rpx;
overflow: hidden;
margin: 10rpx;
background: rgb(244, 245, 246);
position: relative;
border-radius: 10rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.u-preview-wrap {
border: 1px solid rgb(235, 236, 238);
margin-right: 10rpx !important;
}
.u-add-wrap {
flex-direction: column;
color: $u-content-color;
font-size: 26rpx;
}
.u-add-tips {
margin-top: 20rpx;
line-height: 40rpx;
}
.u-add-wrap__hover {
background-color: rgb(235, 236, 238);
}
.u-preview-image {
display: block;
width: 100%;
height: 100%;
border-radius: 10rpx;
}
.u-delete-icon {
position: absolute;
top: 10rpx;
right: 10rpx;
z-index: 10;
background-color: $u-type-error;
border-radius: 100rpx;
width: 44rpx;
height: 44rpx;
@include vue-flex;
align-items: center;
justify-content: center;
}
.u-icon {
@include vue-flex;
align-items: center;
justify-content: center;
}
.u-progress {
position: absolute;
bottom: 10rpx;
left: 8rpx;
right: 8rpx;
z-index: 9;
width: auto;
}
.u-error-btn {
color: #ffffff;
background-color: $u-type-error;
font-size: 20rpx;
padding: 4px 0;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 9;
line-height: 1;
}
</style>

20
index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

91
main.js Normal file
View File

@@ -0,0 +1,91 @@
import App from './App'
import {
addStyle,
navbarHeight,
m_by_checkID,
getAge
} from '@/common/js/util.js'
// 引入uView主JS库
import uView from "uview-ui";
import store from './store';
import share from '@/common/share.js'
Vue.mixin(share)
Vue.use(uView);
let systemInfo = uni.getSystemInfoSync();
const $d = {
addStyle,
navbarHeight,
sys: systemInfo,
getAge
}
Vue.prototype.$toast = (title='', icon = 'none',duration=1500) => {
uni.showToast({
icon,
title,
duration
})
}
Vue.prototype.$go = (url, type = 0) => {
let userInfo = uni.getStorageSync('userInfo') || true;
const whitelist = ['pages', 'pharmacist_setInfo','workbench_succeed']
if (type!=4&&!whitelist.find(item => url.includes(item)) && userInfo && userInfo.status <= 1) {
Vue.prototype.$toast('资质审核通过后可开通')
return
}
let way = 'navigateTo';
switch (type) {
case 0:
way = 'navigateTo';
break;
case 1:
way = 'redirectTo';
break;
case 2:
way = 'reLaunch';
break;
case 3:
way = 'switchTab';
break;
case 4:
way = 'navigateBack';
break;
}
type == 4 ? uni.navigateBack(url) : uni[way]({
url: url,
fail: (e) => {
console.log(e);
}
})
}
// $d挂载到uni对象上
uni.$d = $d
Vue.prototype.$statusBarHeight = systemInfo.statusBarHeight
Vue.prototype.$d = $d
Vue.prototype.$navbarHeight = uni.$d.navbarHeight()
Vue.prototype.$windowHeight = systemInfo.windowHeight
Vue.prototype.$screenHeight = systemInfo.screenHeight
Vue.prototype.$checkID = m_by_checkID
Vue.prototype.$male = require('@/static/image/nan.png')
Vue.prototype.$girl = require('@/static/image/nv.png')
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif

80
manifest.json Normal file
View File

@@ -0,0 +1,80 @@
{
"name" : "xiaokang_doctor",
"appid" : "__UNI__2465184",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* wx8e8f04d0cf7831bf */
"mp-weixin" : {
"appid" : "wx6a823aa229ab67c7",
"setting" : {
"urlCheck" : true,
"es6" : true,
"postcss" : true,
"minified" : true
},
"usingComponents" : true,
"optimization" : {
"subPackages" : true
},
"lazyCodeLoading" : "requiredComponents",
"plugins" : {}
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2"
}

12
package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"id": "cu-editor",
"name": "微信小程序富文本编辑器(仿腾讯文档)",
"version": "1.0.4",
"description": "照着腾讯文档小程序开发了微信小程序富文本编辑器组件这几天做个整理如果有这个需求可以前往腾讯文档小程序操作看看实际效果。腾讯文档小程序用的不是原生的组件当前项目按现有开放api尽可能实现。",
"keywords": [
"editor"
],
"dependencies": {
"uview-ui": "^1.8.8"
}
}

426
pages.json Normal file
View File

@@ -0,0 +1,426 @@
{
"easycom": {
"autoscan": true,
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"^d-(.*)": "@/components/d-$1/d-$1.vue"
},
"pages": [{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/login/register",
"style": {
"navigationBarTitleText": "注册",
"navigationStyle": "custom"
}
},
{
"path": "pages/workbench/index",
"style": {
"navigationBarTitleText": "工作台",
"navigationStyle": "custom"
}
},
{
"path": "pages/workbench/examine",
"style": {
"navigationBarTitleText": "账号审核"
}
},
{
"path": "pages/patient/index",
"style": {
"navigationBarTitleText": "患者",
"navigationStyle": "custom"
}
},
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
},
{
"path": "pages/my/prescriptionList",
"style": {
"navigationBarTitleText": "我的处方"
}
},
{
"path": "pages/workbench/prescriptionList",
"style": {
"navigationBarTitleText": "待流转处方"
}
},
{
"path": "pages/workbench/prescriptionDetail",
"style": {
"navigationBarTitleText": "处方详情"
}
},
{
"path": "pages/my/prescriptionDetail",
"style": {
"navigationBarTitleText": "处方详情"
}
},
{
"path": "pages/pharmacist/index",
"style": {
"navigationBarTitleText": "药师",
"navigationStyle": "custom"
}
},
{
"path": "pages/pharmacist/pharmacist-my",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
}
],
"subPackages": [{
"root": "subPackages/sub_workbench",
"pages": [{
"path": "workbench_upInfo/index",
"style": {
"navigationBarTitleText": "上传资料",
"enablePullDownRefresh ": false, //此设置 是否开启当前页面下拉刷新。
"disableScroll": false //此设置 是否允许页面整体上下滚动。
}
},
{
"path": "workbench_consult",
"style": {
"navigationBarTitleText": "图文咨询",
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
},
{
"path": "workbench_identity",
"style": {
"navigationBarTitleText": "选择身份",
"navigationStyle": "custom"
}
},
{
"path": "workbench_reception",
"style": {
"navigationBarTitleText": "加载中..."
}
},
{
"path": "workbench_succeed",
"style": {
"navigationBarTitleText": "加载中..."
}
},
{
"path": "workbench_announcement",
"style": {
"navigationBarTitleText": "停诊公告"
}
},
{
"path": "workbench_upInfo/select",
"style": {
"navigationBarTitleText": "加载中...",
"enablePullDownRefresh": true,
"backgroundColor": "#000"
}
},
{
"path": "workbench_evaluation",
"style": {
"navigationBarTitleText": "我的评价",
"enablePullDownRefresh": true
}
},
{
"path": "workbench_reason",
"style": {
"navigationBarTitleText": "选择退诊原因"
}
},
{
"path": "workbench_recipe/index",
"style": {
"navigationBarTitleText": "处方详情",
"navigationStyle": "custom"
}
},
{
"path": "workbench_recipe/add",
"style": {
"navigationBarTitleText": "添加商品"
}
},
{
"path": "workbench_recipe/addType2",
"style": {
"navigationBarTitleText": "添加商品"
}
},
{
"path": "workbench_recipe/common",
"style": {
"navigationBarTitleText": "常用方"
}
},
{
"path": "workbench_recipe/commonDetails",
"style": {
"navigationBarTitleText": "常用方详情"
}
},
{
"path": "workbench_basicInfo",
"style": {
"navigationBarTitleText": "基础健康信息"
}
},
{
"path": "workbench_inquiries",
"style": {
"navigationBarTitleText": "问诊记录"
}
},
{
"path": "workbench_used_language",
"style": {
"navigationBarTitleText": "常用语管理"
}
},
{
"path": "workbench_code",
"style": {
"navigationBarTitleText": "医生码"
}
},
{
"path": "workbench_infoPatient",
"style": {
"navigationBarTitleText": "患者详情"
}
}
]
},
{
"root": "subPackages/sub_patient",
"pages": [{
"path": "patient_label",
"style": {
"navigationBarTitleText": "消息",
"navigationStyle": "custom"
}
},
{
"path": "patient_massInfo",
"style": {
"navigationBarTitleText": "群发信息"
}
},
{
"path": "patient_select",
"style": {
"navigationBarTitleText": "群发信息"
}
},
{
"path": "article_select",
"style": {
"navigationBarTitleText": "选择文章"
}
},
{
"path": "patient_details",
"style": {
"navigationBarTitleText": "患者详情"
}
},
{
"path": "to-store",
"style": {
"navigationBarTitleText": "加载中...",
"navigationStyle": "custom"
}
},
{
"path": "patient_detail",
"style": {
"navigationBarTitleText": "患者详情"
}
}
]
},
{
"root": "subPackages/sub_my",
"pages": [{
"path": "my_setAct",
"style": {
"navigationBarTitleText": "加载中...",
"navigationStyle": "custom"
}
},
{
"path": "my_revenue/index",
"style": {
"navigationBarTitleText": "挂号费用",
"navigationStyle": "custom"
}
}, {
"path": "my_revenue/withdraw",
"style": {
"navigationBarTitleText": "申请提现"
}
}, {
"path": "my_revenue/record",
"style": {
"navigationBarTitleText": "提现记录"
}
}, {
"path": "my_revenue/earnings",
"style": {
"navigationBarTitleText": "收益明细"
}
}, {
"path": "my_service",
"style": {
"navigationBarTitleText": "服务设置"
}
},
{
"path": "my_diagnose_common",
"style": {
"navigationBarTitleText": "常用医嘱管理"
}
}
]
},
{
"root": "subPackages/sub_pharmacist",
"pages": [{
"path": "pharmacist_info",
"style": {
"navigationBarTitleText": "个人资料",
"navigationStyle": "custom"
}
},
{
"path": "im",
"style": {
"navigationBarTitleText": "咨询"
}
},
{
"path": "pharmacist_setInfo",
"style": {
"navigationBarTitleText": "设置",
"navigationStyle": "custom"
}
},
{
"path": "change_password",
"style": {
"navigationBarTitleText": "修改登录密码"
}
},
{
"path": "pharmacist_prescription",
"style": {
"navigationBarTitleText": "查看处方",
"navigationStyle": "custom"
}
},
{
"path": "pharmacist_traceability",
"style": {
"navigationBarTitleText": "处方溯源",
"navigationStyle": "custom"
}
},
{
"path": "pharmacist_access",
"style": {
"navigationBarTitleText": "待接入用户"
}
},
{
"path": "pharmacist_triage",
"style": {
"navigationBarTitleText": "选择医生",
"enablePullDownRefresh": true
}
},
{
"path": "myrecord-detail",
"style": {
"navigationBarTitleText": "处方详情"
}
}
]
}, {
"root": "subPackages/sub_agreement",
"pages": [{
"path": "agreement",
"style": {
"navigationBarTitleText": "协议"
}
}, {
"path": "web-view",
"style": {
"navigationBarTitleText": "视频"
}
}]
}
],
"tabBar": {
"color": "#fff",
"selectedColor": "#fff",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [{
"pagePath": "pages/workbench/index",
"text": "工作台"
},
{
"pagePath": "pages/patient/index",
"text": "患者"
},
// {
// "pagePath": "pages/article/index",
// "text": "我的文章"
// },
{
"pagePath": "pages/my/index",
"text": "我的"
},
{
"pagePath": "pages/pharmacist/index",
"text": "审方"
}
]
},
"globalStyle": {
"navigationBarTitleText": "萧康互联网医院",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black",
"backgroundColor": "#f7f8fa"
}
}

396
pages/login/index.vue Normal file
View File

@@ -0,0 +1,396 @@
<template>
<view class="container safe-area-inset-bottom">
<view class="title">
欢迎登录
</view>
<view class="box">
<view class="info">
<view class="pwd">
<d-input :maxlength="11" height="96" v-model="form['mobile']"
:prefixIcon="require('../../static/image/act.png')" borderRadius="96rpx" prefixIconSize="40rpx"
placeholder="请输入手机号" @input="is_d=false" :placeholder-style="{fontSize:'32rpx'}"
:custom-style="{fontSize:'32rpx',paddingLeft:'60rpx'}" />
</view>
<view class="pwd">
<u-field v-model="smsCode" placeholder="请填写验证码" :placeholder-style="{fontSize:'32rpx'}">
<u-button size="mini" slot="right" @click="getCode">{{codeText}}</u-button>
</u-field>
<u-verification-code ref="uCode" @change="codeChange" class="code">
</u-verification-code>
</view>
<view class="flex-row" style="margin-top: 88rpx;">
<u-icon size="35" :color="form['check']?'#6ACDBB':'#e0fdff'" name="checkmark-circle-fill"
@click="form['check']=!form['check']"></u-icon>
<view class="agreement">
我已阅读并同意
<text
@click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">用户服务协议</text>
<text
@click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">隐私权政策</text>
</view>
</view>
<!-- :disabled="!form['mobile']||!form['pwd']" -->
<view class="btn">
<u-button :throttle-time="20" shape="circle" @click="register" :ripple="true" :loading="loading">
登录</u-button>
</view>
<view class="register">
<text @click="$go('../../subPackages/sub_workbench/workbench_identity')">手机号注册</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {
login,
register,
agreement,
getUserInfo,
loginOrRe
} from '../../api/all';
export default {
data() {
return {
loading: false,
form: {
check: false
},
switchVal: false,
is_d: false,
statusBarHeight: this.$statusBarHeight,
navbarHeight: this.$navbarHeight,
mobile: '',
codeText: '',
smsCode: '',
code: ''
};
},
onLoad(e) {
},
mounted() {
const token = uni.getStorageSync('token')
const info = uni.getStorageSync('loginInfo')
if (token&&info) {
// 药师
if (info.role == 2 && info.status > 0 && info!= 3) {
setTimeout(() => {
this.$go('/pages/pharmacist/index', 3)
}, 200)
return
}
// 医生
if (info.role == 1 && info.status > 0 && info.status != 3) {
setTimeout(() => {
this.$go('/pages/workbench/index', 2)
}, 200)
return
}
}
},
methods: {
toLogin() {
uni.login({
provider: 'weixin',
success: (loginRes) => {
this.code = loginRes.code
}
})
},
//登录或者注册接口
register() {
//修改1
if (!uni.$u.test.mobile(this.form['mobile'])) {
this.$toast('请输入正确的手机号')
return
}
if (!this.smsCode) {
this.$toast('请输入验证码')
return
}
if (!this.form['check']) {
this.$toast('请勾选协议')
return
}
const data = {
store_id: '11001',
mobile: this.form['mobile'],
status: 3,
smsCode: this.smsCode,
code: this.code
}
loginOrRe(data).then(res => {
if (res.errcode != -1) {
this.$toast(res.data[0])
uni.setStorageSync('token', res.data['token'])
// console.log(res);
uni.setStorageSync('identity', res.data['ServiceUser']['role'])
uni.setStorageSync('logInfo', res.data['ServiceUser'])
const data = {
store_id: uni.getStorageSync('store_id') || '11001',
}
getUserInfo(data).then(res => {
if (res.errcode != -1) {
uni.setStorageSync('loginInfo', res.data)
uni.setStorageSync('role', res.data.role)
uni.setStorageSync('login_id', res.data['id'])
uni.setStorageSync('login_status', res.data['status'])
}
// 未完善信息
if (res.data.status == 0 ) {
this.$toast('您还没有完善信息,请先完善信息')
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
uni.navigateTo({
url:'../../subPackages/sub_workbench/workbench_upInfo/index'
})
return
}
}).catch(() => {
this.loading = false
this.$toast(res.msg)
})
// 药师
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status ==2 ) {
setTimeout(() => {
this.$go('/pages/pharmacist/index', 3)
}, 600)
return
}
// 医生 res.data.ServiceUser.status > 0 && res.data .ServiceUser.status != 3
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status==2 ) {
setTimeout(() => {
this.$go('/pages/workbench/index', 2)
}, 600)
return
}
// 医生未通过审核
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status == 3) {
// this.$toast(res.data.ServiceUser.reason)
setTimeout(() => {
this.$go('/pages/workbench/examine')
}, 2000)
return
}
// 医生审核中
if (res.data.ServiceUser.role == 1 && res.data.ServiceUser.status == 1) {
this.$toast('您的账号还在审核中')
setTimeout(() => {
this.$go('/pages/workbench/examine')
}, 2000)
return
}
// 药师未通过审核
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status == 3) {
// this.$toast(res.data.ServiceUser.reason)
setTimeout(() => {
this.$go('/pages/workbench/examine')
}, 2000)
return
}
// 药师审核中
if (res.data.ServiceUser.role == 2 && res.data.ServiceUser.status == 1) {
this.$toast('您的账号还在审核中')
setTimeout(() => {
this.$go('/pages/workbench/examine')
}, 2000)
return
}
}
if (res.errcode == -1) {
// this.$toast('您还没有账号,请先注册')
// setTimeout(() => {
// this.$go('../../subPackages/sub_workbench/workbench_identity')
// }, 600)
this.$toast(res.msg)
return
}
})
},
checkboxChange(val) {
// console.log(val);
},
codeChange(text) {
this.codeText = text;
},
getCode() {
if (this.$refs.uCode.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码'
})
setTimeout(() => {
uni.hideLoading();
// 通知验证码组件内部开始倒计时
//发送验证码接口
const data = {
store_id: '11001',
mobile: this.form['mobile']
}
register(data).then(res => {
// console.log(res);
if (res.errcode != -1) {
// this.code = res.data.code
uni.setStorageSync('smsCode', res.data['smsCode'])
// if(this.code==res.data.smsCode){
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
// }else{
// this.$u.toast('验证码错误')
// return
// }
}
})
this.$refs.uCode.start();
}, 1000);
} else {
this.$u.toast('倒计时结束后再发送');
}
}
}
};
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
}
.title {
width: 750rpx;
padding-top: 200rpx;
text-align: center;
font-size: 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #0D111A;
background: linear-gradient(180deg, #EAF2FF 0%, #FFFFFF 100%);
margin-bottom: 60rpx;
}
.flex-row {
display: flex;
flex-direction: row;
margin-bottom: 34rpx;
.agreement {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-left: 16rpx;
text {
color: #2979FF;
size: 24rpx;
}
}
}
.box {
box-sizing: border-box;
padding: 0 52rpx;
}
.code {
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #1777FF;
background: #F9FAFB;
}
.agreement {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-left: 16rpx;
}
.pwd {
width: 646rpx;
height: 96rpx;
background: #F9FAFB;
border-radius: 48rpx 48rpx 48rpx 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #CCCCCC;
margin: 0rpx 0 30rpx;
::v-deep button {
color: #1777FF;
border: 0;
}
}
.hint {
margin-top: 80rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.btn {
::v-deep button {
width: 648rpx;
height: 100rpx;
line-height: 100rpx;
background: #6ACDBB;
border: 0;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
}
.register {
display: flex;
justify-content: center;
align-items: center;
margin-top: 15px;
text {
color: #999;
font-size: 14px;
cursor: pointer;
word-spacing: 3px;
}
}
</style>

299
pages/login/register.vue Normal file
View File

@@ -0,0 +1,299 @@
<template>
<view class="container safe-area-inset-bottom">
<view class="title">
欢迎注册
</view>
<view class="box">
<view class="info">
<view class="pwd">
<d-input :maxlength="11" height="96" v-model="form['mobile']"
:prefixIcon="require('../../static/image/act.png')" borderRadius="96rpx" prefixIconSize="40rpx"
placeholder="请输入手机号" @input="is_d=false" :placeholder-style="{fontSize:'32rpx'}"
:custom-style="{fontSize:'32rpx',paddingLeft:'60rpx'}" />
</view>
<view class="pwd">
<u-field v-model="smsCode" placeholder="请填写验证码" :placeholder-style="{fontSize:'32rpx'}">
<u-button size="mini" slot="right" @click="getCode">{{codeText}}</u-button>
</u-field>
<u-verification-code ref="uCode" @change="codeChange" class="code">
</u-verification-code>
</view>
<view class="flex-row" style="margin-top: 88rpx;">
<u-icon size="35" :color="form['check']?'#6ACDBB':'#e0fdff'" name="checkmark-circle-fill"
@click="form['check']=!form['check']"></u-icon>
<view class="agreement">
我已阅读并同意
<text
@click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')">用户服务协议</text>
<text
@click="$go('/subPackages/sub_agreement/agreement?type=service_privacy_agreement')">隐私权政策</text>
</view>
</view>
<!-- :disabled="!form['mobile']||!form['pwd']" -->
<view class="btn">
<u-button :throttle-time="20" shape="circle" @click="register" :ripple="true" :loading="loading">
注册</u-button>
</view>
<view class="register" @click="$go('/pages/login/index')">
<text>手机号登录</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {
login,
register,
agreement,
getUserInfo,
loginOrRe
} from '../../api/all';
export default {
data() {
return {
loading: false,
form: {
check: false
},
switchVal: false,
is_d: false,
statusBarHeight: this.$statusBarHeight,
navbarHeight: this.$navbarHeight,
mobile: '',
codeText: '',
smsCode: '',
code: '',
role: 1,
};
},
onLoad(e) {
// console.log(e, 'role');
this.role = e.role
},
mounted() {
uni.login({
provider: 'weixin',
success: (loginRes) => {
this.code = loginRes.code
// console.log(loginRes);
uni.setStorageSync('code', loginRes.code)
}
})
},
methods: {
//登录或者注册接口
register() {
if (!uni.$u.test.mobile(this.form['mobile'])) {
this.$toast('请输入正确的手机号')
return
}
if (!this.smsCode) {
this.$toast('请输入验证码')
return
}
if (!this.form['check']) {
this.$toast('请勾选协议')
return
}
const data = {
store_id: '11001',
role: this.role,
plate_type: 2,
mobile: this.form['mobile'],
status: 4,
smsCode: this.smsCode,
code: uni.getStorageSync('code')
}
loginOrRe(data).then(res => {
if (res.errcode != -1) {
uni.setStorageSync('token', res.data['token'])
uni.setStorageSync('logInfo', res.data['user'])
setTimeout(() => {
uni.showToast({
title: '开始注册',
icon: 'success'
})
}, 1000)
//跳转到激活页面
setTimeout(() => {
this.$go('../../subPackages/sub_workbench/workbench_succeed')
}, 800)
} else {
this.$toast(res.msg)
//跳转到激活页面
// setTimeout(() => {
// this.$go('../../subPackages/sub_workbench/workbench_succeed')
// }, 900)
}
// console.log(res);
})
},
checkboxChange(val) {
console.log(val);
},
codeChange(text) {
this.codeText = text;
},
getCode() {
if (this.$refs.uCode.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码'
})
setTimeout(() => {
uni.hideLoading();
// 通知验证码组件内部开始倒计时
//发送验证码接口
const data = {
store_id: '11001',
mobile: this.form['mobile']
}
register(data).then(res => {
console.log(res);
if (res.errcode != -1) {
// this.code = res.data.code
uni.setStorageSync('smsCode', res.data['smsCode'])
// if(this.code==res.data.smsCode){
// this.$go('../../subPackages/sub_workbench/workbench_upInfo/index')
// }else{
// this.$u.toast('验证码错误')
// return
// }
}
})
this.$refs.uCode.start();
}, 1000);
} else {
this.$u.toast('倒计时结束后再发送');
}
}
}
};
</script>
<style lang="scss" scoped>
.container {
width: 100vw;
height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
}
.title {
width: 750rpx;
padding-top: 200rpx;
text-align: center;
font-size: 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #0D111A;
background: linear-gradient(180deg, #EAF2FF 0%, #FFFFFF 100%);
margin-bottom: 60rpx;
}
.flex-row {
display: flex;
flex-direction: row;
margin-bottom: 34rpx;
.agreement {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-left: 16rpx;
text {
color: #2979FF;
size: 24rpx;
}
}
}
.box {
box-sizing: border-box;
padding: 0 52rpx;
}
.code {
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #1777FF;
background: #F9FAFB;
}
.agreement {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
margin-left: 16rpx;
}
.pwd {
width: 646rpx;
height: 96rpx;
background: #F9FAFB;
border-radius: 48rpx 48rpx 48rpx 48rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #CCCCCC;
margin: 0rpx 0 30rpx;
::v-deep button {
color: #1777FF;
border: 0;
}
}
.hint {
margin-top: 80rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.btn {
::v-deep button {
width: 648rpx;
height: 100rpx;
line-height: 100rpx;
background: #6ACDBB;
border: 0;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
}
}
.register {
display: flex;
justify-content: center;
align-items: center;
margin-top: 15px;
text {
color: #999;
font-size: 14px;
cursor: pointer;
word-spacing: 3px;
}
}
</style>

226
pages/my/index.vue Normal file
View File

@@ -0,0 +1,226 @@
<template>
<view class="">
<u-navbar title="我的" :is-back="false" title-size="34" :title-color="parseInt(identity)>1?'#EAF2FF':'#000'"
:border-bottom="false"></u-navbar>
<view v-if="parseInt(identity)==1">
<view class="info flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-a">
<image class="avatar" :src="doctorInfo.DoctorInfo.avatar">
</image>
<view class="flex-col flex-jus-se m-l-32">
<view class="flex-row flex-ali-center">
<d-text :text="doctorInfo.DoctorInfo.name || '微信用户'"
className="m-r-16 main-c fs-36"></d-text>
<u-tag text="审核中" type="warning" size="mini" shape="circle" border-color="#6A77CD"
bgColor="#6A77CD" color="#fff" v-if="doctorInfo.DoctorInfo.user.status==1" />
<u-tag text="已认证" type="warning" size="mini" shape="circle" border-color="#6A77CD"
bgColor="#6A77CD" color="#fff" v-if="doctorInfo.DoctorInfo.user.status==2" />
<u-tag v-if="doctorInfo.DoctorInfo.user.status==3" text="审核未通过" type="warning" size="mini"
border-color="#A7ABB0" bgColor="#A7ABB0" color="#fff" />
</view>
<view class="flex-row" @click="$go('../../subPackages/sub_my/my_setAct')">
<d-text text="个人主页" color="#6C7380"></d-text> &nbsp;
<u-icon name="arrow-right" size="24" color="#A7ABB0"></u-icon>
</view>
</view>
</view>
<view class="button" @click="$go('../../subPackages/sub_pharmacist/pharmacist_info')">
编辑资料
</view>
</view>
<view class="p-row-2">
<!-- <view class="p-32 white b-r-16">
<view class="flex-row" @click="$go('../../subPackages/sub_my/my_revenue/index')">
<d-text text="我的收入" className="main-c fs-32 m-r-16"></d-text>
<u-icon :name="eye?'eye':'eye-off'" color="#000" @click="eye=!eye" size="32"></u-icon>
</view>
<view class="flex-row m-t-32">
<view class="flex-1 flex-col">
<d-text className="tips-c fs-24" text="累计收入(元)"></d-text>
<d-text className="content-c fs-4" text="0.00" :bold="true"></d-text>
</view>
<view class="flex-1 flex-col m-l-24">
<d-text className="tips-c fs-24" text="本月预估收入(元)"></d-text>
<d-text className="content-c fs-4" text="0.00" :bold="true"></d-text>
</view>
</view>
</view> -->
<view class="grid-tem-col-3 m-t-2 white p-col-32">
<view class="flex-col flex-jus-sp flex-ali-center" v-for="(item,i) in functions"
@click="$go(item.url)" :key="i">
<image class="fun" :src="item.icon"></image>
<d-text color="#31353D" :text="item.name"></d-text>
</view>
</view>
<view class="flex-col m-t-2 white p-row-32">
<view class="flex-row bottom-border flex-jus-sp p-col-32 flex-ali-center" v-for="(item,i) in items"
:key="i" @click="$go(item.url)">
<view class="flex-row flex-ali-center">
<image class="item" :src="item.icon" mode="aspectFit"></image>
<d-text :text="item.name" className="content-c m-l-16"></d-text>
</view>
<u-icon name="arrow-right" color="#bcbcbc"></u-icon>
</view>
</view>
</view>
</view>
<my-pharmacist v-if="parseInt(identity)==2" :info="userInfo" :avatar="getAvatar"></my-pharmacist>
<my-service v-if="parseInt(identity)>2" :info="userInfo" :avatar="getAvatar"></my-service>
<d-tabbar></d-tabbar>
</view>
</template>
<script>
import {
info,
personalData,
leadInfo,
servInfo
} from "@/api/all.js"
import myPharmacist from './my-pharmacist.vue'
import myService from './my-service.vue'
export default {
components: {
myService,
myPharmacist
},
data() {
return {
userInfo: uni.getStorageSync('userInfo') || {},
functions: [{
// name: '我的评价 我的常用方',
// icon: '../../static/image/wdpj.png',
// url: '../../subPackages/sub_workbench/workbench_evaluation'
// }, {
name: '我的处方',
icon: '../../static/image/wd-cf.png',
url: '/pages/my/prescriptionList'
},
{
name: '我的常用方',
icon: '../../static/image/wd-cyf.png',
url: '../../subPackages/sub_workbench/workbench_recipe/common?type=0'
},
// {
// name: '挂号费用',
// icon: '../../static/image/wd-ghfy.png',
// url: '../../subPackages/sub_my/my_revenue/index'
// },
{
name: '服务设置',
icon: '../../static/image/fwsz.png',
url: '../../subPackages/sub_my/my_service'
}
],
items: [
// {
// name: '常用语管理',
// icon: '../../static/image/wj.png',
// url: '../../subPackages/sub_my/my_commonReply/index'
// }, {
// name: '账号设置我的评价',
// icon: '../../static/image/set.png',
// url: '../../subPackages/sub_pharmacist/pharmacist_setInfo'
// },
// {
// name: '我的评价',
// icon: '../../static/image/wj.png',
// url: '../../subPackages/sub_workbench/workbench_evaluation'
// },
{
name: '常用医嘱管理',
icon: '../../static/image/i1.png',
url: '../../subPackages/sub_my/my_diagnose_common'
}
],
eye: false,
identity: uni.getStorageSync('identity') || (uni.getStorageSync('userInfo') || {}).role || 1,
userInfo: uni.getStorageSync('userInfo') || {},
is_req: false,
doctorInfo: []
};
},
computed: {
// getAvatar() {
// let doctorInfo = uni.getStorageSync('doctorInfo')
// let url = require(`@/static/image/${doctorInfo.DoctorInfo.user.role%2==0?'nv':'nan'}.png`);
// // console.log(info, 'ddd')
// return doctorInfo.DoctorInfo['avatar'] ? doctorInfo.DoctorInfo['avatar'] : doctorInfo
// .DoctorInfo.user.role == 1 ? url : url
// }
},
created() {
this.getInfo()
},
onHide() {
},
onShow() {
this.getInfo()
},
methods: {
async getInfo() {
let res = {}
this.identity == 1 && (res = await info({
store_id: uni.getStorageSync('store_id') || '11001'
}));
if (res.errcode == 0) {
this.doctorInfo = res.data
uni.setStorageSync('userInfo', res.data.DoctorInfo)
} else {
this.$toast(res.msg);
}
}
},
mounted() {
this.getInfo()
},
onShow() {
this.getInfo()
}
};
</script>
<style>
page {
background-color: #F9FAFB;
}
</style>
<style lang="scss" scoped>
.info {
background: linear-gradient(180deg, #EAF2FF 0%, rgba(238, 245, 255, 0) 100%);
padding: 32rpx 20rpx;
.button {
width: 128rpx;
height: 50rpx;
border-radius: 198rpx;
border: 1rpx solid #6ACDBB;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6ACDBB;
text-align: center;
line-height: 50rpx;
}
}
.fun {
width: 80rpx;
height: 80rpx;
margin-bottom: 10rpx;
vertical-align: middle;
}
.item {
width: 40rpx;
height: 40rpx;
}
.avatar {
width: 128rpx;
height: 128rpx;
background: #D9D9D9;
border: 4rpx solid #FFFFFF;
border-radius: 128rpx;
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<view class="">
<view class="p-2">
<view class="p-32 white">
<view class="flex-row">
<u-image width="96rpx" height="96rpx" shape="circle" :src="avatar">
<u-loading slot="loading"></u-loading>
</u-image>
<view class="flex-col flex-jus-sp m-l-32">
<d-text :text="info.name" :bold="true"></d-text>
<d-text :text="info.idcard.replace(/\d{4}$/, '****')" >
</d-text>
</view>
</view>
<view class="organization m-t-32">
<d-text text="执业机构" color="#31353D" :bold="true"></d-text>
<d-text className="content-c m-t08" :text="`${info.idcard.replace(/^\d{2}/, '**')} ${info.yardes[0].name}`">
</d-text>
</view>
</view>
<view class="p-32 white m-t-12" @click="$go('../../subPackages/sub_pharmacist/pharmacist_info?avatar='+avatar+'&info='+JSON.stringify(info))">
<view class="flex-row flex-jus-sp flex-ali-center">
<u-icon margin-left="20" label-size="32" label-color="#31353D"
:name="require('../../static/image/grzl.png')" label="个人资料" size="30"></u-icon>
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
</view>
</view>
<view class="p-32 white m-t-2" @click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
<view class="flex-row flex-jus-sp flex-ali-center">
<u-icon margin-left="20" label-size="32" label-color="#31353D"
:name="require('../../static/image/set.png')" label="设置" size="30"></u-icon>
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props:{
avatar:{
type:String,
default:''
},
info:{
type:Object,
default:()=>{
return {}
}
}
},
data() {
return {
};
},
onLoad() {
},
methods: {
}
};
</script>
<style>
page {
background-color: #F9FAFB;
}
</style>
<style lang="scss" scoped>
.organization {
background-color: #F9FAFB;
padding: 16rpx;
}
</style>

85
pages/my/my-service.vue Normal file
View File

@@ -0,0 +1,85 @@
<template>
<view class="p-2">
<view class="p-32 white">
<view class="flex-row">
<u-image width="128rpx" height="128rpx" shape="circle" :src="avatar">
<u-loading slot="loading"></u-loading>
</u-image>
<view class="flex-col flex-jus-sp m-l-32">
<d-text :text="info.name||''" className="fs-36" :bold="true"></d-text>
<d-text :text="identity==3?'导医':'客服'" :bold="true"></d-text>
</view>
</view>
</view>
<view class="p-row-32 b-r-8 white m-t-2">
<view class="p-col-32" @click="$go('../../subPackages/sub_my/my_commonReply/index')">
<view class="flex-row flex-jus-sp flex-ali-center">
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="chat" label="快捷回复" size="40"
color="#6C7380"></u-icon>
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
</view>
</view>
</view>
<view class="p-row-32 b-r-8 white m-t-2">
<view class="p-col-32 flex-jus-sp flex-row flex-ali-center bottom-border" @click="$go()">
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="kefu-ermai" label="联系客服" size="40"
color="#6C7380"></u-icon>
<view class="flex-row flex-ali-center">
<view class="m-r-16 flex-row flex-jus-center flex-ali-center badge">
<u-badge type="error" count="7" :absolute="false"></u-badge>
</view>
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
</view>
</view>
<view class="p-col-32" @click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
<view class="flex-row flex-jus-sp flex-ali-center">
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="setting" label="设置" size="40"
color="#6C7380"></u-icon>
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
avatar: {
type: String,
default: ''
},
info: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
identity: (uni.getStorageSync('userInfo') || {}).role || 3,
};
},
onLoad() {
},
methods: {
},
options: {
styleIsolation: 'shared'
},
};
</script>
<style>
page {
background-color: #F9FAFB;
}
</style>
<style lang="scss">
::v-deep .u-badge {
display: flex !important;
position: relative;
}
</style>

View File

@@ -0,0 +1,777 @@
<template>
<view class="container safe-area-inset-bottom">
<!-- 处方 -->
<view class="head">
<view class="head_record_top">
<view class="record">
处方编号{{infoList.prescription_no}}
</view>
<view class="record_state">
{{lists.type==1 ? '普通':'常用'}}
</view>
</view>
<!-- 医院 -->
<view class="head_record">
<image :src="lists.store.offical_seal||'/static/group/xkyard.png'" mode=""></image>
<view class="record_yard">
<view class="yard">
{{lists.store.name}}
</view>
<view class="state">
处方笺
</view>
</view>
</view>
<!-- 时间 -->
<view class="head_record_time">
<text style="text-align: right;">开具日期{{infoList.created_at}}</text>
</view>
</view>
<!-- 信息 -->
<view class="my_info">
<view class="info">
<view class="info_item">
<view class="name">
姓名
<text>{{infoList.patient.name}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
性别
<text>{{infoList.patient.sex%2==0?'女':'男'}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
年龄
<text>{{infoList.patient.age}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
类别
<text>{{infoList.category}}</text>
</view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="name">
科室
<text>{{infoList.doctor.depart.name}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
电话
<text>{{infoList.patient.mobile}}</text>
</view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="names">
诊断
<text>{{infoList.clinical_diagnose}}</text>
</view>
</view>
</view>
</view>
<view class="bg">
<!-- 药品 -->
<view class="my_medical">
<view class="title">
Rp
</view>
<block v-if="lists.prescription_type==1||lists.prescription_type==3">
<view class="items" v-for="(item,index) in infoList.repice" :key="item.id">
<!-- v-for="(it,index) in item.content" :key="it.id" -->
<view class="name">
<view class="_name" v-for="(it,index) in item.content" :key="it.id">
<view class="text">
<text>{{ it.name}}</text>
<text class="_abbr" v-if="it.order">[{{useWay[it.order]}}]</text>
</view>
<text class="name_num">{{ it.number}}{{it.unit?it.unit.name:'g'}}</text>
</view>
</view>
<view class="details">
<view class="text">
<text>用法煎服每天 {{item.consumption}} </text>
<!-- <text v-if="item.deployment==2">{{' 每次 '+item.volume+' ml '}} </text> -->
<text> {{item.dosage}} </text>
</view>
</view>
</view>
</block>
<block v-if="lists.prescription_type==2">
<view class="item" v-for="(it,index) in infoList.repice" :key="item.id">
<!-- v-for="(it,index) in item.content" :key="it.id" -->
<view class="name" style="justify-content: space-between;">
<view class="yp_name">
<text>{{ it.content.drug_name}} </text>
<text style="font-size: 24rpx;margin-left: 10rpx;">{{ it.content.specification }}</text>
</view>
<text>x{{ it.number}}</text>
</view>
<view class="details">
<text>用法{{ it.instruction }}</text>
</view>
</view>
</block>
</view>
<!-- 医嘱 -->
<view class="doctor_order">
<view class="yz">
<text class="order_info_left">医嘱:</text>
<view class="order_info">
<view class="info" v-for="(item ,index) in lists.doctor_order" :key="index">
{{item}}
</view>
</view>
</view>
<view class="titles">
处方开具已完毕
</view>
</view>
</view>
<!-- 医生 -->
<view class="my_doctor">
<view class="doctor_info">
<view class="info">
<text>医师</text>
<!-- <text class="name">{{infoList.doctor.name || ''}}</text> -->
<image :src="'data:image/jpeg;base64,'+lists.doctor_sign_image" mode="aspectFit"></image>
<image v-if="lists.doctor_second_sign == 1" :src="'data:image/jpeg;base64,'+lists.doctor_sign_image" mode="aspectFit"></image>
</view>
<view class="info">
<text>审核药师</text>
<!-- <text class="name">{{rpList.name || ' '}}</text> -->
<image :src="'data:image/jpeg;base64,'+lists.Pharmacist_sign_image" mode="aspectFit"></image>
</view>
<view class="info">
<text>发药人</text>
<image :src="'data:image/jpeg;base64,'+lists.Pharmacist_sign_image" mode="aspectFit"></image>
</view>
<view class="info">
<text>核对人</text>
<image :src="'data:image/jpeg;base64,'+lists.Pharmacist_sign_image" mode="aspectFit"></image>
</view>
<view class="info">
<text>调配人</text>
<image :src="'data:image/jpeg;base64,'+lists.Pharmacist_sign_image" mode="aspectFit"></image>
</view>
</view>
<view class="price">
价格 {{infoList.total_pay_price}}
</view>
</view>
<!-- 温馨提示 -->
<view class="my_prompt">
<view class="title">
温馨提示请遵医嘱服药处方{{lists.valid_hours}}小时有效
</view>
<image src="/static/group/img-yzf.png" mode="" v-if="status==2"></image>
</view>
<view v-if="notice_id" class="rotation" @click.stop="rotation"> </view>
<!-- 状态 -->
</view>
</template>
<script>
import {
rotationApi,
prescripDetail,
getUseWay
} from "@/api/all.js";
export default {
data() {
return {
statusName: {
0: '待审核',
1: '已通过',
2: '未通过',
3: '待使用',
4: '已使用',
5: '未使用',
6: '已失效',
7: '已初审'
},
// status:flase,
status: true,
// status: "", // 处方状态
status: "", // 处方状态
id: "",
infoList: [],
num: 1,
rpList: [],
order_id: "",
lists: [],
useWay: [],
notice_id: ''
}
},
onLoad(e) {
// console.log('12')
// console.log(e, "id");
this.id = e.id
this.notice_id = e.notice_id
this.getInfo()
getUseWay({
store_id: uni.getStorageSync('store_id') || 11001,
}).then((res) => {
if (res.errcode == 0) {
const useWay = ["无", ...(res.data.map(it => it.name))]
uni.setStorageSync('useWay', JSON.stringify(useWay))
this.useWay = useWay
}
})
},
methods: {
async rotation() {
rotationApi({
notice_id: this.notice_id,
store_id: uni.getStorageSync('store_id') || '11001'
}).then(res => {
uni.showToast({
title: '转方成功',
icon: 'success',
mask: true
})
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1500)
})
},
// 数量
getnum(obj) {
let list = obj
let num = 0
list.map((it) => {
num += it.number
})
return num
},
getInfo() {
prescripDetail({
store_id: uni.getStorageSync('store_id') || '11001',
prescription_id: this.id
}).then((res) => {
// console.log(res, 'deta');
if (res.errcode == 0) {
this.infoList = res.data.content
this.rpList = res.data.pharmacistInfo
this.lists = res.data
this.status = res.data.status
// console.log(this.rpList, 'info');
}
})
},
// getDrug(obj) {
// console.log(obj, 'obj')
// if (!obj) {
// return
// }
// let list = (obj.chinese.length > 0 ? obj.chinese : obj.west) || []
// let drugs = []
// // console.log(list);
// for (let i = 0; i < list.length; i++) {
// if (obj.chinese.length > 0) {
// for (let j = 0; j < JSON.parse(list[i].content).length; j++) {
// drugs.push(JSON.parse(list[i].content)[j]['name'])
// }
// } else {
// drugs.push(JSON.parse(list[i].content)['name'])
// }
// }
// return drugs.join()
// },
},
mounted() {
// this.getInfo()
}
}
</script>
<style lang="scss" scoped>
.container {
width: 750rpx;
min-height: 100vh;
max-height: 100%;
padding-bottom: env(safe-area-inset-bottom);
.bg {
min-height: calc(100vh - 600rpx);
}
.head {
.head_record_top {
width: 710rpx;
margin: 14rpx auto 0;
display: flex;
align-items: center;
justify-content: space-between;
.record {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.record_state {
width: 80rpx;
height: 42rpx;
line-height: 42rpx;
text-align: center;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
border: 1rpx solid #C4C7CC;
}
}
.head_record_time {
text-align: right;
width: 750rpx;
padding: 0 32rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.head_record {
display: flex;
align-items: center;
justify-content: center;
position: relative;
image {
width: 160rpx;
height: 150rpx;
position: absolute;
left: 40%;
top: -8rpx;
}
.record_yard {
text-align: center;
.yard {
font-size: 46rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
.state {
height: 44rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
}
}
}
.head_record_time {
text-align: right;
margin-top: 20rpx;
width: 750rpx;
padding-right: 32rpx;
height: 34rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
line-height: 28rpx;
}
}
// 信息
.my_info {
width: 710rpx;
margin: 4rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 4rpx 4rpx;
.info {
width: 710rpx;
display: flex;
align-items: center;
.info_item {
.names {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 0rpx 0 8rpx;
// flex-wrap: nowrap;
// display: -webkit-box;
// -webkit-line-clamp: 1;
// -webkit-box-orient: vertical;
// text-overflow: ellipsis;
// overflow: hidden;
}
}
.name {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 26rpx 0 6rpx;
}
}
}
}
}
// 药品
.my_medical {
width: 710rpx;
margin: 4rpx auto;
padding: 1rpx 10rpx;
.title {
font-size: 32rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
color: #31353D;
margin-bottom: 2rpx;
}
.item {
width: 710rpx;
padding: 1rpx 0;
.name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
._name {
display: flex;
justify-content: space-between;
margin: 2rpx 80rpx 2rpx 0;
.text {
margin-right: 16rpx;
display: flex;
flex-direction: column;
._abbr {
color: #A7ABB0;
font-size: 20rpx;
margin-top: 2rpx;
}
}
}
}
.details {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
display: flex;
flex-direction: column;
justify-content: space-around;
.text {
text {
word-spacing: 1rpx;
}
}
}
}
.items {
width: 710rpx;
padding: 1rpx 0;
.name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
._name {
display: flex;
justify-content: space-between;
margin: 2rpx 80rpx 2rpx 0;
.text {
margin-right: 26rpx;
display: flex;
flex-direction: column;
._abbr {
color: #A7ABB0;
font-size: 20rpx;
margin-top: 2rpx;
}
}
}
}
.details {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
display: flex;
flex-direction: column;
justify-content: space-around;
.text {
text {
word-spacing: 1rpx;
}
}
}
}
}
// 医嘱
.doctor_order {
width: 710rpx;
margin: 2rpx auto;
padding: 0 10rpx;
word-spacing: 2rpx;
.yz {
display: flex;
width: 100%;
color: #6C7380;
.order_info_left {
margin-right: 3rpx;
}
.order_info {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
line-height: 40rpx;
margin-bottom: 2rpx;
.info {
flex: 1;
}
}
}
.titles {
width: 710rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
margin: 8rpx 0 20rpx;
}
}
// 医生
.my_doctor {
width: 710rpx;
margin: 2rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
padding: 2rpx 4rpx;
.doctor_info {
width: 710rpx;
display: flex;
align-items: center;
margin-bottom: 4rpx;
flex-wrap: wrap;
.info {
min-width: 222rpx;
max-width: 400rpx;
margin: 5rpx 0 8rpx;
text {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
image {
width: 82rpx;
height: 62rpx;
vertical-align: middle;
}
.name {
width: 106rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
border-bottom: 2rpx solid #000000;
margin-left: 16rpx;
}
}
}
.price {
width: 710rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
}
// 温馨提示
.my_prompt {
width: 710rpx;
height: 304rpx;
margin: 0 auto;
padding: 0 4rpx;
position: relative;
.title {
width: 710rpx;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
line-height: 40rpx;
margin: 32rpx 0;
}
image {
width: 159rpx;
height: 100rpx;
position: absolute;
left: 60%;
top: 0%;
}
.prompt {
width: 710rpx;
height: 200rpx;
text {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
}
ol {
padding: 0 0 0 32rpx;
li {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
margin: 4rpx 0;
}
}
}
}
// 状态
.my_state {
width: 710rpx;
height: 40rpx;
margin: 12rpx auto;
padding-bottom: 110rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #F44336;
line-height: 40rpx;
}
.line {
border-bottom: 2rpx solid #31353D;
}
}
.rotation {
position: fixed;
bottom: 30rpx;
right: 24rpx;
left: 24rpx;
height: 80rpx;
display: flex;
font-size: 30rpx;
align-items: center;
justify-content: center;
background: #5792f9;
color: #fff;
border-radius: 12rpx;
}
</style>

View File

@@ -0,0 +1,192 @@
<template>
<view class="">
<view class="box safe-area-inset-bottom">
<!-- tabs -->
<view class="tabs">
<u-tabs bar-width="30" :height="59" :list="tabs" :current="current" :is-scroll="false" @change="change"
active-color="#6ACDBB"></u-tabs>
</view>
<!-- -->
<view class="p-row-2 m-t-2">
<block v-if="!loading && list.length > 0">
<view class="p-32 b-r-8 white m-b-2" v-for="(item, i) in list" :key="item.id"
@click="$go('/pages/my/prescriptionDetail?id=' +item.id)">
<view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<u-icon margin-left="8" label-size="32" label-color="#6C7380"
:name="require('../../static/image/ys.png')" :label="item.prescription_no"
color="#6ACDBB" size="32"></u-icon>
<d-text :text="statusName[item.status]"></d-text>
</view>
<view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<d-text className="content-c fs-32" :text="getInfo(item.patient)"></d-text>
</view>
<view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<d-text :text="$u.timeFormat(item.created_at, 'yyyy-mm-dd')"></d-text>
</view>
</view>
</block>
<view class="flex-row flex-jus-center" v-if="!loading && list.length == 0">
<d-empty text="暂无信息" mode="search"></d-empty>
</view>
</view>
</view>
<d-loading-page :loading="loading"></d-loading-page>
</view>
</template>
<script>
import {
prescripDetailList
} from "@/api/all.js";
export default {
data() {
return {
list: [],
page: 1,
loading: true,
loading2: true,
status: "loading",
totalPage: 1,
loadText: {
loadmore: "轻轻上拉",
loading: "努力加载中",
nomore: "已加载全部",
},
statusName: [
"待审核",
"已通过",
"未通过",
"待使用",
"已使用",
"未使用",
"已失效",
],
tabs: [{
name: "全部"
},
{
name: "待审核"
},
{
name: "未通过"
},
{
name: "已通过"
},
],
current: 0,
status: ""
};
},
created() {},
onShow() {
this.getList();
},
onHide() {},
onReachBottom() {
if (this.loading2) {
return;
}
if (this.page >= this.totalPage) {
this.$toast("数据已全部加载");
return;
}
this.page++;
this.getList();
},
methods: {
getInfo(user) {
return `${user.name} ${user.sex == 1 ? "男" : "女"} ${user.age}`;
},
change(index) {
// console.log(index);
this.current = index;
if (this.current == 0) {
this.status = ''
}
if (this.current == 1) {
this.status = 0
}
if (this.current == 2) {
this.status = 2
}
if (this.current == 3) {
this.status = 1
}
this.getList()
},
async getList() {
try {
this.loading2 = true;
const res = await prescripDetailList({
page: this.page,
store_id: uni.getStorageSync('store_id') || '11001',
status: this.status
});
if (res.errcode === 0) {
this.list =
this.page === 1 ? res.data.list : [...this.list, ...res.data.list];
this.totalPage = res.data.pagination.totalPage;
} else {
this.$toast(res.msg);
}
this.loading = false;
this.loading2 = false;
} catch (error) {
this.loading = false;
this.loading2 = false;
}
},
},
};
</script>
<style>
page {
background-color: #f9fafb;
}
</style>
<style lang="scss" scoped>
.tabs{
background-color: #fff;
width: 750rpx;
position: sticky;
top: 0rpx;
z-index: 9999999;
padding-top: 12rpx;
}
.box {
position: relative;
z-index: 982;
image {
width: 32rpx;
height: 32rpx;
}
}
::v-deep .box {
.u-tab-item {
line-height: 1 !important;
}
}
.state {
margin-top: 160rpx;
image {
width: 300rpx;
height: 223rpx;
}
}
.search {
padding: 16rpx 32rpx;
}
</style>

237
pages/patient/index.vue Normal file
View File

@@ -0,0 +1,237 @@
<template>
<view class="page">
<u-navbar height="66" title="患者中心" :is-back="false" title-size="34" title-color="#fff"
:background="{background:'linear-gradient(90deg, #6ACDBB 0%, #6ACDBB 93%)'}">
</u-navbar>
<view class="bg" :style="{top:`${statusBarHeight+navbarHeight-1}px`}"></view>
<view class="search p-row-32">
<u-search placeholder="请输入患者姓名" @input="handleSearch" v-model="keyword" :show-action="false"></u-search>
</view>
<view class="box p-row-2 m-t-2">
<view class="flex-row flex-jus-sp">
<view class="function white b-r-8 flex-row p-32 flex-1"
@click="$go('../../subPackages/sub_patient/to-store?id='+1)">
<image src="../../static/image/hz_xsjz.png"></image>
<view class="m-l-16 l-19">
<d-text text="到店接诊" size="32" color="#31353D"></d-text>
</view>
</view>
<view class="function white b-r-8 flex-row p-32 flex-1 m-l-2"
@click="$go('../../subPackages/sub_patient/to-store?id='+2)">
<image src="../../static/image/hz_ddjz.png"></image>
<view class="m-l-16 l-19">
<d-text text="线上接诊" size="32" color="#31353D"></d-text>
</view>
</view>
</view>
<view class="list-new" @click="$go('../../subPackages/sub_patient/patient_massInfo')">
<view class="l-l-row">
<image src="/static/image/hz-qfxx.png" mode=""></image>
<d-text text="群发消息" className="main-c m-r-16 fs-32"></d-text>
</view>
<u-icon name="arrow-right" color="#333333"></u-icon>
</view>
<view class="flex-col b-r-8 white p-32 m-t-2">
<view class="title flex-row flex-jus-sp">
<d-text text="患者管理" className="main-c m-r-16 fs-32"></d-text>
<!-- <u-icon label="筛选" size="40" label-size="28" margin-right="10" label-color="#6C7380" :name="require('@/static/image/sx.png')">
</u-icon> -->
<!-- <u-icon v-if="!isSearch" @click="isSearch = true" name="search" size="40rpx" color="#333"></u-icon> -->
<!-- <view v-else class="flex-row flex-con flex-jus-sp flex-ali-center">
<d-input @input="handleSearch" class="flex-con m-r08" v-model="keyword"
:custom-style="{fontSize:'28rpx'}" placeholder="请输入搜索内容" />
<u-icon @click="isSearch = false" name="close-circle-fill" size="40rpx" color="#999"></u-icon>
</view> -->
</view>
<view v-for="(item, index) in list" :key="index" class="flex-row flex-ali-center m-t-50"
@click="toDetail(item.id)">
<u-image width="80rpx" height="80rpx" shape="circle"
:src="item.avatar || require(`../../static/image/${item.sex%2==0?'nv':'nan'}.png`)">
<u-loading slot="loading"></u-loading>
</u-image>
<view class="flex-col m-l-2 flex-jus-sp">
<view class="flex-row flex-ali-end">
<d-text :text="item.patient" className="content-c fs-32"></d-text>
<d-text :text="item.age + '岁'" className="fs-28 tips-c m-l-2"></d-text>
</view>
<!-- <view class="flex-row flex-ali-center">
<view class="m-r08">
<u-tag text="高血糖" size="mini" border-color="rgba(41,121,255,0.08)"
bg-color="rgba(41,121,255,0.08)" color="#6ACDBB" />
</view>
<view class="m-r08">
<u-tag text="高血压" size="mini" border-color="rgba(41,121,255,0.08)"
bg-color="rgba(41,121,255,0.08)" color="#6ACDBB" />
</view>
</view> -->
</view>
</view>
</view>
</view>
<d-tabbar></d-tabbar>
</view>
</template>
<script>
import {
getPatientListApi
} from "@/api/all.js";
export default {
data() {
return {
male: this.$male,
girl: this.$girl,
keyword: '',
navbarHeight: this.$navbarHeight,
statusBarHeight: this.$statusBarHeight,
list: [],
page: 1,
loading: true,
loading2: false,
status: "loading",
loadText: {
loadmore: "轻轻上拉",
loading: "努力加载中",
nomore: "已加载全部",
},
totalPage: 1,
};
},
onShow() {
this.init();
},
onPullDownRefresh() {
this.init();
},
onReachBottom() {
if (this.page >= this.totalPage) {
this.$toast("数据已全部加载");
return;
}
this.page++;
this.getList();
},
methods: {
init() {
this.list = [];
this.page = 1;
this.keyword = '';
this.loading = true;
this.getList(true);
},
handleSearch() {
this.page = 1
this.getList()
},
getList(is_stop = false) {
if (this.loading2) {
return;
}
this.loading2 = true;
getPatientListApi({
page: this.page,
name: this.keyword,
store_id: uni.getStorageSync('store_id') || '11001',
status: 1
})
.then((res) => {
if (res.errcode == 0) {
this.list = this.page === 1 ? res.data.list : [...this.list, ...res.data.list];
this.totalPage = res.data.pagination.totalPage;
if (this.page >= this.totalPage) {
this.status = "nomore";
} else {
this.status = "loadmore";
}
this.$nextTick(() => {
this.loading = false;
is_stop && uni.stopPullDownRefresh();
});
} else {
this.$toast(res.msg);
is_stop && uni.stopPullDownRefresh();
this.loading = false;
if (this.page >= this.totalPage) {
this.status = "nomore";
} else {
this.status = "loadmore";
}
}
})
.finally(() => {
this.loading2 = false;
});
},
toDetail(id) {
uni.navigateTo({
url: '/subPackages/sub_patient/patient_detail?id=' + id
})
}
}
};
</script>
<style>
page {
background-color: #eeeeef;
}
</style>
<style lang="scss" scoped>
.bg {
position: fixed;
left: 0;
width: 750rpx;
height: 204rpx;
background: linear-gradient(90deg, #6ACDBB 0%, #6ACDBB 93%);
z-index: 981;
}
.search {
position: relative;
z-index: 982;
}
.box {
position: relative;
z-index: 982;
}
.function {
image {
width: 80rpx;
height: 80rpx;
}
}
.l-19 {
display: flex;
align-items: center;
}
.list-new {
width: 710rpx;
height: 108rpx;
background: #FFFFFF;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
margin: 20rpx 0;
padding: 0 32rpx;
color: #333333;
.l-l-row {
display: flex;
align-items: center;
image {
width: 56rpx;
height: 56rpx;
margin-right: 12rpx;
vertical-align: middle;
}
}
}
</style>

332
pages/pharmacist/index.vue Normal file
View File

@@ -0,0 +1,332 @@
<template>
<view class="safe-area-inset-bottom">
<u-navbar title="审方" :is-back="false" title-size="34" title-color="#fff" :background="{
background:'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)'}">
</u-navbar>
<!-- 头部 tabs -->
<view class="head">
<u-tabs :list="lists" :is-scroll="false" :current="currents" @change="change" active-color="#6ACDBB"
:height="99">
</u-tabs>
</view>
<!-- 空页 -->
<view class="empty" v-if="listInfo.length==0 ||status!=2">
<image src="/static/image/none.png" mode="aspectFit"></image>
<text v-if="status!=2">您还没有通过审核请稍后再试~</text>
</view>
<!-- 列表 -->
<block v-if="status==2">
<!-- 西药 -->
<!-- <view class="lists" v-for="(item,index) in listWest" :key="item.id"
@click="toDetail(item.id,item.prescription_no)">
<view class="info">
<view class="bh">
<image src="/static/image/tubiao1.png" mode=""></image>
{{item.prescription_no}}
</view>
<view class="name">
{{item.patients.name}} {{(item.patients.sex)%2==0?'女':'男'}} {{item.patients.age}}
</view>
<view class="time">
{{item.created_at | dateFormat}}
</view>
</view>
<view class="states" :class="item.status==1?'tg':'wtg'">
<view class="tag_states">
<u-tag text="西药" type="primary" />
</view>
{{item.status==0&&'待审核' || item.status==1&&'已通过' || item.status==2&&'未通过'}}
</view>
</view> -->
<!-- 中药 -->
<!-- <view class="lists" v-for="(item,index) in listChinese" :key="item.id"
@click="toDetail(item.id,item.prescription_no)">
<view class="info">
<view class="bh">
<image src="/static/image/tubiao1.png" mode=""></image>
{{item.prescription_no}}
</view>
<view class="name">
{{item.patients.name}} &nbsp; {{(item.patients.sex)%2==0?'女':'男'}} &nbsp; {{item.patients.age}}
</view>
<view class="time">
{{item.created_at | dateFormat}}
</view>
</view>
<view class="states" :class="item.status==1?'tg':'wtg'">
<view class="tag_states">
<u-tag text="中药" type="success" />
</view>
{{item.status==0&&'待审核' || item.status==1&&'已通过' || item.status==2&&'未通过'}}
</view>
</view> -->
<!-- 颗粒 -->
<view class="lists" v-for="(item,index) in listInfo" :key="item.id"
@click="toDetail(item.id,item.prescription_no)">
<view class="info">
<view class="bh">
<image src="/static/image/tubiao1.png" mode=""></image>
{{item.prescription_no}}
</view>
<view class="name">
{{item.patients.name}} &nbsp; {{(item.patients.sex)%2==0?'女':'男'}} &nbsp; {{item.patients.age}}
</view>
<view class="time">
{{item.created_at | dateFormat}}
</view>
</view>
<view class="states" :class="item.status==1?'tg':'wtg'">
<view class="tag_states">
<u-tag text="颗粒" type="warning" v-if="item.prescription_type==3" />
<u-tag text="中药" type="success" v-if="item.prescription_type==1" />
<u-tag text="西药" type="primary" v-if="item.prescription_type==2" />
</view>
{{item.status==0&&'待审核' || item.status==1&&'已通过' || item.status==2&&'未通过'}}
</view>
</view>
</block>
<d-loading-page :loading="loading"></d-loading-page>
<u-tabbar v-model="current" :list="listed" active-color="#6ACDBB" @change="changed"></u-tabbar>
</view>
</template>
<script>
import {
getUserInfo,
prescripRecord
} from "@/api/all.js";
export default {
data() {
return {
status: uni.getStorageSync('login_status'),
keyword: "",
show: false,
identity: uni.getStorageSync("identity") || 2,
index: 0,
loading: true,
userInfo: uni.getStorageSync("userInfo") || {},
listInfo: [], // 中药
// listWest: [], // 西药
// listGranular: [], // 颗粒
waitList: [],
// is_req: false,
listed: '',
current: 0,
lists: [{
name: '待审核'
},
{
name: '已通过',
},
{
name: '未通过',
}
],
currents: 0, // 审核状态
};
},
computed: {
},
onLoad() {
this.listed = [{
iconPath: require("../../static/image/w-sf.png"),
selectedIconPath: require("../../static/image/sf.png"),
text: '审方',
// pagePath: '/pages/pharmacist/index',
current: 0
},
{
iconPath: require("../../static/image/w-wd.png"),
selectedIconPath: require("../../static/image/wd.png"),
text: '我的',
// pagePath: '/pages/pharmacist/pharmacist-my',
}
]
},
onHide() {
// this.is_req = true;
},
onShow() {
this.current = 0
this.getList()
if (this.status != 2) {
this.getKnow()
}
},
methods: {
change(index) {
this.currents = index;
this.getList()
},
changed(index) {
this.current = index;
if (this.current == 1) {
this.$go('/pages/pharmacist/pharmacist-my')
}
},
async getList() {
let res = {};
res = await prescripRecord({
store_id: uni.getStorageSync('store_id') || '11001',
status: this.currents
})
// this.listChinese = [], this.listWest = [], this.listGranular = [];
if (res.errcode == 0) {
// console.log(res, 'lb');
this.listInfo = res.data.prescription
// this.listWest = (res.data.west).reverse();
// this.listGranular = (res.data.granular).reverse()
this.$nextTick(() => {
this.loading = false;
});
} else {
this.$toast(res.msg);
this.loading = false;
this.listInfo = []
}
},
// 详情
toDetail(id, no) {
uni.navigateTo({
url: '../../subPackages/sub_pharmacist/myrecord-detail?id=' + id + '&prescription_no=' + no
})
},
getKnow() {
const data = {
store_id: uni.getStorageSync('store_id') || '11001',
}
getUserInfo(data).then(res => {
if (res.errcode != -1) {
uni.setStorageSync('loginInfo', res.data)
uni.setStorageSync('role', res.data.role)
uni.setStorageSync('login_id', res.data['id'])
uni.setStorageSync('login_status', res.data['status'])
}
})
},
},
mounted() {
if (this.status != 2) {
this.$go('/pages/workbench/examine')
}
},
filters: {
//过滤器 用于格式化时间
dateFormat: function(value) {
var date = new Date(value * 1000); //时间戳为10位需*1000时间戳为13位的话不需乘1000
var year = date.getFullYear();
var month = ("0" + (date.getMonth() + 1)).slice(-2);
var sdate = ("0" + date.getDate()).slice(-2);
var hour = ("0" + date.getHours()).slice(-2);
var minute = ("0" + date.getMinutes()).slice(-2);
var second = ("0" + date.getSeconds()).slice(-2);
// 拼接
var result = year + '-' + month + '-' + sdate + ' ' + hour + ":" + minute //+ ":" + second;
// 返回
return result;
},
},
}
</script>
<style>
page {
background-color: #f9fafb;
}
</style>
<style lang="scss" scoped>
.u-tabbar {
z-index: 999;
}
.head {
background-color: #fff;
width: 750rpx;
height: 99rpx;
position: sticky;
top: 180rpx;
z-index: 9999;
}
.empty {
width: 440rpx;
height: 393rpx;
margin: 200rpx auto;
image {
width: 100%;
height: 100%;
z-index: 10;
}
}
.lists {
width: 750rpx;
padding: 24rpx 36rpx;
background: #FFFFFF;
margin: 30rpx auto 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
font-family: PingFang SC-Regular, PingFang SC;
.info {
.bh {
font-weight: 400;
color: #666666;
image {
width: 38rpx;
height: 38rpx;
vertical-align: middle;
margin-right: 18rpx;
}
}
.name {
font-weight: bold;
color: #333333;
margin: 18rpx 0;
}
.time {
width: 100%;
font-weight: 400;
color: #999999;
display: flex;
justify-content: space-between;
align-items: center;
}
}
.states {
font-weight: 400;
color: #6A6DCD;
display: flex;
flex-direction: column;
.tag_states {
margin-bottom: 44rpx;
}
}
.wtg {
color: #FF477E;
}
.tg {
color: #6ACDBB;
}
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<view class="safe-area-inset-bottom">
<u-navbar title="我的" :is-back="false" title-size="34" title-color="#fff" :background="{
background:'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)'}">
</u-navbar>
<view class="head">
<view class="info">
<image :src="list.avatar || '/static/image/nv.png'" mode=""></image>
<view class="name">
<text>{{list.name}}</text>
<text>药师</text>
</view>
</view>
<view class="store">
职业机构
</view>
<view class="store">
{{list.store.name || ' '}}
</view>
</view>
<view class="card">
<view class="setup" @click="toInfo">
个人资料
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
</view>
<view class="setup" @click="toSetup">
设置
<u-icon name="arrow-right" color="#A7ABB0"></u-icon>
</view>
</view>
<u-tabbar v-model="current" :list="listed" active-color="#6ACDBB" @change="changed"></u-tabbar>
<d-loading-page :loading="loading"></d-loading-page>
</view>
</template>
<script>
import {
my
} from "@/api/all.js";
export default {
data() {
return {
keyword: "",
show: false,
identity: uni.getStorageSync("identity") || 2,
index: 0,
navbarHeight: this.$navbarHeight,
statusBarHeight: this.$statusBarHeight,
loading: true,
userInfo: uni.getStorageSync("userInfo") || {},
list: [],
waitList: [],
is_req: false,
current: 1,
listed: '',
};
},
computed: {
},
created() {
},
onShow() {
},
onLoad() {
this.listed = [{
iconPath: require("../../static/image/w-sf.png"),
selectedIconPath: require("../../static/image/sf.png"),
text: '审方',
// pagePath: '/pages/pharmacist/index',
current: 0
},
{
iconPath: require("../../static/image/w-wd.png"),
selectedIconPath: require("../../static/image/wd.png"),
text: '我的',
// pagePath: '/pages/pharmacist/pharmacist-my',
current: 1
}
]
},
methods: {
changed(index) {
this.current = index;
// console.log(this.current);
if (this.current == 0) {
uni.switchTab({
url: '/pages/pharmacist/index',
})
}
},
async getList() {
let res = {};
res = await my({
store_id: uni.getStorageSync('store_id') || '11001',
})
this.list = [];
if (res.errcode == 0) {
// console.log(res, 'wd');
this.list = res.data;
this.$nextTick(() => {
this.loading = false;
});
} else {
this.$toast(res.msg);
this.loading = false;
}
},
toInfo() {
this.$go('../../subPackages/sub_pharmacist/pharmacist_info')
},
toSetup() {
this.$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')
}
},
mounted() {
this.getList()
}
};
</script>
<style>
page {
background-color: #f9fafb;
}
</style>
<style lang="scss" scoped>
.bg {
position: fixed;
width: 750rpx;
height: 400rpx;
// background: linear-gradient(
// 180deg,
// #00BFA6 0%,
// #6ACDBB 0%,
// rgba(41, 121, 255, 0) 100%
// );
z-index: 981;
}
.head {
font-family: PingFang SC-Bold, PingFang SC;
font-weight: bold;
color: #232323;
width: 710rpx;
height: 304rpx;
background: #FFFFFF;
margin: 0 auto;
padding: 18rpx 20rpx;
.info {
display: flex;
align-items: center;
margin-bottom: 40rpx;
image {
border-radius: 50%;
width: 116rpx;
height: 116rpx;
}
.name {
display: flex;
flex-direction: column;
text {
margin: 10rpx 20rpx;
}
}
}
.store {
height: 50rpx;
line-height: 50rpx;
}
}
.card {
width: 710rpx;
height: 176rpx;
background: #FFFFFF;
margin: 30rpx auto;
font-family: PingFang SC-Bold, PingFang SC;
font-weight: bold;
color: #232323;
.setup {
height: 88rpx;
line-height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
}
}
</style>

267
pages/workbench/examine.vue Normal file
View File

@@ -0,0 +1,267 @@
<template>
<view class="container safe-area-inset-bottom" :style="{ height: screenHeight }">
<image class="image" src="/static/login/bg_shh.png" mode=""></image>
<view class="bg">
<view class="img">
<image :src="doctorInfo.DoctorInfo.avatar || doctorInfo.avatar" mode=""></image>
</view>
<view class="box">
<view class="name">
<text>{{doctorInfo.DoctorInfo.name || doctorInfo.name}}</text>{{doctorInfo.DoctorInfo.title.name || doctorInfo.titles.name}}
</view>
<view class="store">
{{doctorInfo.store.store.name || doctorInfo.store.name}}
{{doctorInfo.DoctorInfo.depart.name || doctorInfo.depart.name || ''}}
</view>
<image src="/static/login/bg_shhz.png" mode=""></image>
<!-- doctorInfo.DoctorInfo.user.status<=1 -->
<view class="state" v-if="role==1">
{{ doctorInfo.DoctorInfo.user.status<=1&&'您的申请已提交请耐心等待感谢您的配合' ||doctorInfo.DoctorInfo.user.status==3&&'抱歉您的申请已被驳回'}}
</view>
<view class="state" v-if="role==2">
{{ doctorInfo.user.status<=1&&'您的申请已提交请耐心等待感谢您的配合' ||doctorInfo.user.status==3&&'抱歉您的申请已被驳回'}}
</view>
<view class="store" v-if="role==1">
<text v-if="doctorInfo.DoctorInfo.user.status<=1">申请提交后预计1~2个工作日返回结果</text>
<view class="_bx" v-if="doctorInfo.DoctorInfo.user.status==3">
<text style="color: #FF0000;">驳回原因</text>
<text>{{doctorInfo.ServiceUser.reason}}</text>
</view>
</view>
<view class="store" v-if="role==2">
<text v-if="doctorInfo.user.status<=1">申请提交后预计1~2个工作日返回结果</text>
<view class="_bx" v-if="doctorInfo.user.status==3">
<text style="color: #FF0000;">驳回原因</text>
<text>{{doctorInfo.user.reason}}</text>
</view>
</view>
</view>
</view>
<button class="btn" v-if="doctorInfo.DoctorInfo.user.status==3 && role==1"
@click="toNew(doctorInfo.DoctorInfo.su_id)">重新申请</button>
<button class="btn" v-if="doctorInfo.user.status==3 && role==2" @click="toNew(doctorInfo.su_id)">重新申请</button>
</view>
</template>
<script>
import {
failNew,
getUserInfo,
info,
personalData
} from '@/api/all.js'
export default {
data() {
return {
doctorInfo: [],
screenHeight: 0,
role: uni.getStorageSync('role'),
// status: 1,
reason: "",
};
},
onLoad() {
// this.$go('../../subPackages/sub_workbench/workbench_recipe/index')
// console.log(1);
this.screenHeight = uni.getSystemInfoSync().windowHeight
},
onShow() {
if (this.role == 1) {
this.getInfo()
}
if (this.role == 2) {
this.getPerInfo()
}
},
methods: {
// 医生详情
getInfo() {
info({
store_id: uni.getStorageSync('store_id') || '11001'
}).then((res) => {
if (res.errcode == 0) {
this.doctorInfo = res.data
if (res.data.user.status == 2) {
setTimeout(() => {
this.$go('/pages/workbench/index', 2)
}, 20000)
}
}
})
},
// 药师详情
getPerInfo() {
personalData({
store_id: uni.getStorageSync('store_id') || '11001'
}).then((res) => {
if (res.errcode == 0) {
this.doctorInfo = res.data
if (res.data.user.status == 2) {
setTimeout(() => {
this.$go('/pages/pharmacist/index', 2)
}, 20000)
}
}
})
},
// // 状态
// getStatus() {
// getUserInfo({
// store_id: uni.getStorageSync('store_id') || '11001',
// }).then((res) => {
// if (res.errcode ==0) {
// uni.setStorageSync('loginInfo', res.data)
// this.status=res.data.status
// this.reason = res.data.reason
// }
// })
// },
toNew(e) {
failNew({
store_id: uni.getStorageSync('store_id') || '11001',
id: e
}).then((res) => {
if (res.errcode == 0) {
this.$toast('申请成功,请重新注册')
setTimeout(() => {
uni.navigateTo({
url:'../../subPackages/sub_workbench/workbench_upInfo/index'
})
}, 100)
}
})
},
getNewinfo() {
if (this.role == 1 && uni.getStorageSync('login_status') != 2) {
setTimeout(() => {
this.getInfo()
}, 100000)
} else {
setTimeout(() => {
this.getPerInfo()
}, 100000)
}
}
},
mounted() {
this.getNewinfo()
// this.getStatus()
},
};
</script>
<style>
page {
background-color: #eeeeef;
width: 100%;
height: 100%;
}
</style>
<style lang="scss" scoped>
.container {
width: 100vw;
min-height: 100vh;
padding: 0;
margin: 0;
position: relative;
.image {
width: 100vw;
min-height: 100vh;
max-height: 100%;
}
.bg {
z-index: 9999999999 !important;
position: absolute;
left: 13%;
top: 120rpx;
text-align: center;
border-radius: 36rpx;
background: #F8FAFF;
.img {
width: 180rpx;
height: 180rpx;
margin: -80rpx auto 20rpx;
image {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.box {
z-index: 9999999;
width: 560rpx;
font-family: PingFang SC-Regular, PingFang SC;
color: #1E293B;
padding: 20rpx 28rpx;
.name {
font-size: 28rpx;
font-weight: 400;
text {
font-size: 36rpx;
margin-right: 10rpx;
font-weight: bold;
color: #000000;
}
margin: 8rpx 0;
}
.store {
font-size: 24rpx;
font-weight: 400;
color: #666666;
line-height: 48rpx;
margin: 5rpx auto;
._bx {
display: flex;
flex-direction: column;
}
}
.state {
font-size: 28rpx;
font-weight: bold;
color: #333333;
line-height: 48rpx;
margin: 0 auto 50rpx;
}
image {
width: 215rpx;
height: 241rpx;
vertical-align: middle;
margin: 68rpx 0;
}
}
}
}
.btn {
width: 380rpx;
height: 84rpx;
background: #FFFFFF;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
border: 0;
border-radius: 64rpx;
text-align: center;
line-height: 80rpx;
position: absolute;
left: 40%;
margin-left: -130rpx;
bottom: 13%;
}
</style>

750
pages/workbench/index.vue Normal file
View File

@@ -0,0 +1,750 @@
<template>
<view>
<view class="bg" :style="{height:`calc(${navbarHeight}px + ${navbarHeight}px + 394rpx)`}">
</view>
<view class="navbar" :style="{ height: statusBarHeight+navbarHeight + 'px' }">
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<view class="text-align-cen" :style="{ height:navbarHeight + 'px' ,lineHeight: navbarHeight + 'px'}">
工作台
</view>
</view>
<view class="box" :style="{marginTop: statusBarHeight+navbarHeight + 'px'}">
<view class="store_" @click="openShow=true">
{{doctorInfo.store.store.name || '默认门店'}}
<u-icon name="map-fill" color="#fff" size="28" margin-left="12"></u-icon>
</view>
<view class="box_info">
<view class="box_info_left">
<image :src="doctorInfo.DoctorInfo.avatar" style="width: 116rpx;height:116rpx ;border-radius: 50%;"
mode="aspectFill">
</image>
<view class="flex-col flex-jus-sp" style="height: 106rpx;margin-left: 32rpx;">
<view class="flex-row flex-ali-center">
<text class="box_info_left_name">{{doctorInfo.DoctorInfo.name}}</text>
<text class="m-r-16">{{doctorInfo.DoctorInfo.title['name']}}</text>
<u-tag text="审核中" type="warning" size="mini" shape="circle" border-color="#FB8C00"
bgColor="#FB8C00" color="#fff" v-if="doctorInfo.DoctorInfo.user.status<=1" />
<u-tag text="已认证" type="warning" size="mini" shape="circle" border-color="#FB8C00"
bgColor="#FB8C00" color="#fff" v-if="doctorInfo.DoctorInfo.user.status==2" />
<u-tag v-if="doctorInfo.DoctorInfo.user.status==3" text="审核未通过" type="warning" size="mini"
border-color="#A7ABB0" bgColor="#A7ABB0" color="#fff" />
</view>
<text>
{{`${doctorInfo.store.store.name}${doctorInfo.DoctorInfo.depart['name']}`}}
</text>
</view>
</view>
<view class="box_info_right flex-col flex-jus-sp flex-ali-center"
@click="$go('../../subPackages/sub_workbench/workbench_code')">
<image src="../../static/image/mp.png"></image>
<d-text text="我的名片" className="w-c fs-2"></d-text>
</view>
</view>
<view class="box_data flex-row flex-jus-sp flex-ali-center">
<view class="flex-col flex-jus-sp flex-ali-center">
<text class="box_data_nub">{{doctorInfo.wait_accept || '0'}}</text>
<label>待接诊</label>
</view>
<view class="flex-col flex-jus-sp flex-ali-center">
<text class="box_data_nub">{{doctorInfo.accepting || '0'}}</text>
<label>已接诊</label>
</view>
</view>
</view>
<view class="content">
<view class="function p-32">
<view class="function_grid">
<view class="function_item flex-col flex-ali-center flex-jus-sp"
@click="doctorInfo.DoctorInfo.user.status==3?$go('../../subPackages/sub_workbench/workbench_upInfo/index'):$go(item.url)"
v-for="(item,i) in functions" :key="i">
<image :src="item.icon"></image>
<view>{{item.name}}</view>
<u-badge :count="i===0?doctorInfo.wait_accept:0" v-if="i===0" :offset="[-6,98]"
bgColor="#F44336"></u-badge>
</view>
<view class="function_item flex-col flex-ali-center flex-jus-sp" @click="toNext">
<image src="/static/image/xsfz.png"></image>
<view>复诊开药</view>
</view>
</view>
</view>
<block>
<view class="news b-r-8">
<view class="news_title flex-row">
<view class="m-t-32">消息通知</view>
</view>
<!-- height:calc(100vh - 282rpx) -->
<scroll-view scroll-y="true" style="padding: 20rpx 0rpx;" @scrolltolower="scrolltolower"
@refresherrefresh="page=1;status = 'loading';loading = true;infoList=[];$store.dispatch('getDocSession')"
:refresher-triggered="loading" :refresher-enabled="true" v-if="!loading">
<!-- <view class="news_item flex-row flex-jus-sp flex-ali-center" v-for="(item) in infoList"
:key="item.id"
@click="$go(`../../subPackages/sub_workbench/workbench_infoPatient?id=`+item.user_patient_id)">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle"
:src="item.avatar||require(`../../static/image/${item.sex%2==0?'nv':'nan'}.png`)">
<u-loading slot="loading"></u-loading>
</u-image>
<u-badge :count="item.message.no_read" v-if="item.message.no_read>0" :offset="[0,0]"
bgColor="#F44336" :overflow-count="9999"></u-badge>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text :text="item.patient" className="content-c fs-32 m-r-16"></d-text>
</view>
<d-text :text="item.created_at" size="24"></d-text>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text
style="font-size: 24rpx;margin-right: 20rpx;">{{item.sex %2 != 0 ? '男' : '女'}}</text>
<text style="font-size: 22rpx;">{{item.age + '岁'}}</text>
</view>
<d-text :text="states[item.status].name" :color="states[item.status].color"
size="24"></d-text>
</view>
</view>
</view> -->
<!-- 系统消息 评论我的 患者消息 -->
<view class="news_item flex-row flex-jus-sp flex-ali-center"
@click="$go('../../subPackages/sub_patient/patient_label?type=1')" v-if="systemList.num>0">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle" src="/static/image/home1.png">
<u-loading slot="loading"></u-loading>
</u-image>
<u-badge :count="systemList.num" v-if="systemList.num>0" :offset="[0,0]"
bgColor="#F44336" :overflow-count="9999"></u-badge>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text text="系统消息" className="content-c fs-32 m-r-16"></d-text>
</view>
<d-text :text="systemList.time" size="24"></d-text>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text style="font-size: 24rpx;margin-right: 20rpx;"
v-if="systemList.num>0">你有{{systemList.num}}条未读消息</text>
</view>
</view>
</view>
</view>
<view class="news_item flex-row flex-jus-sp flex-ali-center"
@click="$go('../../subPackages/sub_patient/patient_label?type=2')" v-if="infoList.num>0">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle" :src="'/static/image/home2.png'">
<u-loading slot="loading"></u-loading>
</u-image>
<u-badge :count="infoList.num" v-if="infoList.num>0" :offset="[0,0]" bgColor="#F44336"
:overflow-count="9999"></u-badge>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text text="患者消息" className="content-c fs-32 m-r-16"></d-text>
</view>
<d-text :text="infoList.time" size="24"></d-text>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text style="font-size: 24rpx;margin-right: 20rpx;"
v-if="infoList.num>0">你有{{infoList.num}}名患者待接诊</text>
</view>
</view>
</view>
</view>
<!-- <u-loadmore :status="status" :load-text="loadText"
v-if="loading&&infoList.length!=0||!loading" /> -->
<view class="news_item flex-row flex-jus-sp flex-ali-center"
v-if="!loading&&systemList.length==0"
@click="$go('../../subPackages/sub_patient/patient_label?type=1')">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle" src="/static/image/home1.png">
<u-loading slot="loading"></u-loading>
</u-image>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text text="系统消息" size="34" color="#000"></d-text>
</view>
<d-text :text="systemList.time" size="24"></d-text>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text style="font-size: 28rpx;margin-right: 20rpx;">点击查看历史消息</text>
</view>
</view>
</view>
</view>
<!-- 患者 -->
<view class="news_item flex-row flex-jus-sp flex-ali-center" v-if="!loading&&infoList.length==0"
@click="$go('../../subPackages/sub_patient/patient_label?type=2')">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle" :src="'/static/image/home2.png'">
<u-loading slot="loading"></u-loading>
</u-image>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text text="患者消息" size="34" color="#000"></d-text>
</view>
<d-text :text="infoList.time" size="24"></d-text>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text style="font-size: 28rpx;margin-right: 20rpx;">点击查看历史消息</text>
</view>
</view>
</view>
</view>
<!-- 处方 -->
<view v-if="noticeList.length" class="news_item flex-row flex-jus-sp flex-ali-center"
@click="$go('./prescriptionList')">
<view style="width: 80rpx;position: relative;">
<u-image width="80rpx" height="80rpx" shape="circle" src="/static/image/home1.png">
<u-loading slot="loading"></u-loading>
</u-image>
<u-badge :count="noticeList.length" :offset="[0,0]"
bgColor="#F44336" :overflow-count="9999"></u-badge>
</view>
<view class="news_item_info flex-col flex-jus-sp">
<view class="flex-row flex-jus-sp flex-ali-center">
<view class="flex-row flex-ali-center">
<d-text text="待流转处方消息" className="content-c fs-32 m-r-16"></d-text>
</view>
</view>
<view class="flex-row flex-jus-sp">
<view class="news_item_info_msg text-hide">
<text style="font-size: 24rpx;margin-right: 20rpx;">你有{{noticeList.length}}条未读消息</text>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 暂无信息 -->
<!-- <view class="flex-row flex-jus-center" v-if="!loading&&infoList.length==0&&systemList.length==0">
<d-empty text="暂无信息" mode="search"></d-empty>
</view> -->
</view>
<view @click="call" class="phone">咨询客服</view>
<view class="m-b-32 p-t-32"></view>
</block>
</view>
<!-- 切换门店 -->
<u-popup v-model="openShow" mode="bottom" border-radius="24" length="65%" :safe-area-inset-bottom="true">
<view class="content_popup">
<view class="top">
<view class="title">选择诊所</view>
<view class="search p-row-32">
<u-search placeholder="请输入诊所名称" @input="handleSearch" v-model="keyword"
:show-action="false"></u-search>
</view>
</view>
<scroll-view scroll-y="true" style="height: 600rpx;">
<view>
<view class="store_list" v-for="(item,index) in storeList" :key="item.id"
@click="changeStore(item.id)">
{{item.name}}
<u-icon size="35" color="#6ACDBB" name="checkmark-circle-fill"
v-if="store_id==item.id"></u-icon>
</view>
</view>
</scroll-view>
<view class="confrim-btn">
<view class="u-btn qx" @click="openShow=false">取消</view>
<view class="u-btn qd" @click="sure">确定</view>
</view>
</view>
</u-popup>
<d-tabbar></d-tabbar>
</view>
</template>
<script>
import {
chooseList,
hospitalList,
info,
noticeApi,
currentList,
homeSession,
getNotice
} from '@/api/all.js'
import {
registerList
} from '../../api/consult';
export default {
data() {
return {
noticeList: [],
loading: false,
status: 'loading',
loadText: {
loadmore: '轻轻上拉',
loading: '努力加载中',
nomore: '实在没有了'
},
functions: [{
name: '到店接诊',
icon: '../../static/image/zxzx.png',
url: '../../subPackages/sub_workbench/workbench_consult?type=1'
}],
states: {
1: {
name: '待接诊',
color: '#F44336'
},
2: {
name: '接诊中',
color: '#00C853'
},
3: {
name: '已结束',
color: '#C4C7CC'
},
3: {
name: '已取消',
color: '#C4C7CC'
}
},
state: true,
doctorInfo: [],
current: 0,
navbarHeight: this.$navbarHeight,
statusBarHeight: this.$statusBarHeight,
list: [],
page: 1,
totalPage: 1,
openShow: false,
keyword: "",
store: "",
storeList: [],
store_id: uni.getStorageSync('store_id') || '11001',
infoList: [],
systemList: [],
see_rate: "",
};
},
watch: {
docSession: {
deep: true,
immediate: false,
handler(e) {
this.list = e.list;
this.totalPage = e.pagination.totalPage;
if (this.page >= this.totalPage) {
this.status = 'nomore'
} else {
this.status = 'loadmore'
}
this.$nextTick(() => {
this.loading = false
})
}
}
},
computed: {
},
onLoad() {
// this.$go('../../subPackages/sub_workbench/workbench_recipe/index')
// console.log(1);
},
onShow() {
this.getNotice()
this.getInfo()
this.getStore()
this.getList()
},
methods: {
call () {
uni.makePhoneCall({
phoneNumber: '18157126670',
success: (result) => {},
fail: (error) => {}
})
},
scrolltolower() {
if (this.loading) {
this.loading = false
return
}
if (this.page >= this.totalPage) {
this.status = 'nomore'
return
}
this.loading = true
this.status = 'loading'
this.page++
},
getNotice() {
getNotice({
page_size: 9999,
store_id: uni.getStorageSync('store_id') || '11001'
}).then(res => {
if (res.errcode === 401) {
uni.clearStorageSync()
uni.reLaunch({ url: '/pages/login/index' })
}
this.noticeList = res.data.list
})
},
// 医生详情
getInfo() {
info({
store_id: uni.getStorageSync('store_id') || '11001'
}).then((res) => {
// console.log(res.data, 'work-res');
if (res.errcode == 0) {
this.doctorInfo = res.data
this.see_rate = res.data.store.store.see_rate
// console.log(this.see_rate,'sssss');
uni.setStorageSync('see_rate', this.see_rate)
uni.setStorageSync('doctorInfo', this.doctorInfo)
}
})
},
// 搜索门店
handleSearch() {
this.getStore()
},
// 门店列表
getStore() {
currentList({
store_id: uni.getStorageSync('store_id') || '11001',
keyword: this.keyword
}).then((res) => {
// console.log(res, 'store');
if (res.errcode == 0) {
this.storeList = res.data.list
}
})
},
// 切换门店
changeStore(id) {
this.store_id = id
},
sure() {
chooseList({
store_id: uni.getStorageSync('store_id') || '11001',
store: this.store_id
}).then((res) => {
// console.log(res, 'changstore');
if (res.errcode == 0) {
this.$toast(res.data[0])
uni.setStorageSync('store_id', this.store_id)
} else {
this.$toast(res.msg || res.data[0])
this.store_id = uni.getStorageSync('store_id')
}
this.openShow = false
this.getInfo()
})
},
// 消息列表
getList() {
homeSession({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.errcode == 0) {
this.systemList = (res.data.system) || []
this.infoList = (res.data.patient) || []
} else {
this.$toast(res.msg);
}
this.$nextTick(() => {
this.loading = false
})
});
},
// 跳转到小程序
toNext() {
uni.navigateToMiniProgram({
appId: 'wx8e8f04d0cf7831bf',
path: 'pages/login/index?id=',
envVersion: "release",
extraData: {
'data1': 'test'
},
success: res => {
// 打开成功
console.log("打开成功", res);
},
fail: err => {
console.log(err);
}
})
}
},
mounted() {
// this.getInfo()
// this.getStore()
// this.getList()
},
};
</script>
<style>
page {
background-color: #eeeeef;
}
</style>
<style lang="scss" scoped>
.bg {
width: 100vw;
background: linear-gradient(90deg, #6ACDBB 0%, #6ACDBB 93%);
position: fixed;
left: 0;
top: 0;
z-index: 0;
}
.navbar {
background: linear-gradient(90deg, #6ACDBB 0%, #6ACDBB 93%);
width: 100vw;
position: fixed;
left: 0;
top: 0;
font-size: 34rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #FFFFFF;
z-index: 99999;
}
.content {
padding: 0 20rpx;
position: relative;
z-index: 2;
margin-top: 30rpx;
}
.content_popup {
padding: 24rpx;
.top {
height: 200rpx;
.search {
position: relative;
z-index: 982;
}
.title {
height: 54rpx;
line-height: 54rpx;
font-size: 32rpx;
text-align: center;
font-family: PingFang SC-Bold, PingFang SC;
font-weight: bold;
color: #000000;
margin-bottom: 20rpx;
}
}
.store_list {
padding: 0 30rpx;
height: 100rpx;
font-size: 32rpx;
font-family: PingFang SC-Bold, PingFang SC;
font-weight: bold;
color: #333333;
display: flex;
align-items: center;
justify-content: space-between;
}
.confrim-btn {
width: 100%;
height: 100rpx;
padding: 0 30rpx;
margin-top: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
.u-btn {
width: 300rpx;
height: 84rpx;
line-height: 80rpx;
text-align: center;
border-radius: 44rpx 44rpx 44rpx 44rpx;
}
.qx {
border: 2rpx solid #6ACDBB;
color: #6ACDBB;
}
.qd {
background: #6ACDBB;
color: #FFFFFF;
}
}
}
.box {
position: relative;
padding: 0 32rpx;
height: 318rpx;
&_info {
display: flex;
align-items: center;
flex-wrap: nowrap;
justify-content: space-between;
&_left {
display: flex;
align-items: center;
align-content: flex-start;
flex-wrap: nowrap;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
font-size: 28rpx;
&_name {
font-size: 36rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
margin-right: 16rpx;
}
}
&_right {
height: 106rpx;
image {
width: 72rpx;
height: 72rpx;
}
}
}
&_data {
width: 686rpx;
height: 116rpx;
font-size: 24rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
color: #FFFFFF;
margin-top: 50rpx;
padding: 0 32rpx;
view {
height: 84rpx;
min-width: 120rpx;
}
&_nub {
font-size: 36rpx;
}
}
.store_ {
font-size: 28rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 500;
color: #FFFFFF;
margin-bottom: 20rpx;
.u-icon {
margin-left: 30rpx;
}
}
}
.function {
background-color: #fff;
border-radius: 16rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #323233;
&_grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 86rpx;
}
&_item {
position: relative;
width: 292rpx;
height: 144rpx;
}
image {
width: 96rpx;
height: 96rpx;
}
}
.news {
background-color: #fff;
margin-top: 20rpx;
max-height: 100vh;
&_title {
height: 82rpx;
font-size: 32rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
color: #0D111A;
padding: 0 26rpx;
box-sizing: border-box;
text {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
}
&_item {
margin-top: 12rpx;
padding: 26rpx 20rpx;
position: relative;
&_info {
height: 92rpx;
width: 562rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
font-size: 32rpx;
&_msg {
font-size: 28rpx;
width: 462rpx;
color: #6C7380;
}
}
}
}
.phone {
color: #999;
text-align: center;
padding-top: 20rpx;
font-size: 24rpx;
}
</style>

View File

@@ -0,0 +1,764 @@
<template>
<view class="container safe-area-inset-bottom">
<!-- 处方 -->
<view class="head">
<view class="head_record_top">
<view class="record">
处方编号{{infoList.prescription_no}}
</view>
<view class="record_state">
{{lists.type==1 ? '普通':'常用'}}
</view>
</view>
<!-- 医院 -->
<view class="head_record">
<image src="@/static/image/hlwyy.png" mode=""></image>
<view class="record_yard">
<view class="yard">
银川慧疗互联网医院
</view>
<view class="state">
处方笺
</view>
</view>
</view>
<!-- 时间 -->
<view class="head_record_time">
<text style="text-align: right;">开具日期{{infoList.created_at}}</text>
</view>
</view>
<!-- 信息 -->
<view class="my_info">
<view class="info">
<view class="info_item">
<view class="name">
姓名
<text>{{infoList.patient.name}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
性别
<text>{{infoList.patient.sex%2==0?'女':'男'}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
年龄
<text>{{infoList.patient.age}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
类别
<text>{{infoList.category}}</text>
</view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="name">
科室
<text>{{infoList.doctor.depart.name}}</text>
</view>
</view>
<view class="info_item">
<view class="name">
电话
<text>{{infoList.patient.mobile}}</text>
</view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="names">
诊断
<text>{{infoList.clinical_diagnose}}</text>
</view>
</view>
</view>
</view>
<view class="bg">
<!-- 药品 -->
<view class="my_medical">
<view class="title">
Rp
</view>
<block v-if="lists.prescription_type==1||lists.prescription_type==3">
<view class="items" v-for="(item,index) in infoList.repice" :key="item.id">
<!-- v-for="(it,index) in item.content" :key="it.id" -->
<view class="name">
<view class="_name" v-for="(it,index) in item.content" :key="it.id">
<view class="text">
<text>{{ it.name}}</text>
<text class="_abbr" v-if="it.order">[{{useWay[it.order]}}]</text>
</view>
<text class="name_num">{{ it.number}}{{it.unit?it.unit.name:'g'}}</text>
</view>
</view>
<view class="details">
<view class="text">
<text>用法煎服每天 {{item.consumption}} </text>
<!-- <text v-if="item.deployment==2">{{' 每次 '+item.volume+' ml '}} </text> -->
<text> {{item.dosage}} </text>
</view>
</view>
</view>
</block>
<block v-if="lists.prescription_type==2">
<view class="item" v-for="(it,index) in infoList.repice" :key="item.id">
<!-- v-for="(it,index) in item.content" :key="it.id" -->
<view class="name" style="justify-content: space-between;">
<view class="yp_name">
<text>{{ it.content.drug_name}} </text>
<text style="font-size: 24rpx;margin-left: 10rpx;">{{ it.content.specification }}</text>
</view>
<text>x{{ it.number}}</text>
</view>
<view class="details">
<text>用法{{ it.instruction }}</text>
</view>
</view>
</block>
</view>
<!-- 医嘱 -->
<view class="doctor_order">
<view class="yz">
<text class="order_info_left">医嘱:</text>
<view class="order_info">
<view class="info" v-for="(item ,index) in lists.doctor_order" :key="index">
{{item}}
</view>
</view>
</view>
<view class="titles">
处方开具已完毕
</view>
</view>
</view>
<view class="my_doctor">
<view class="doctor_info">
<view class="info">
<text>医师</text>
<text class="name">{{infoList.doctor.name}}</text>
</view>
<view class="info">
<text>初审药师</text>
<text class="name">{{infoList.first_view||''}}</text>
</view>
<view class="info">
<text>复审药师</text>
<text class="name">{{infoList.again_view||''}}</text>
</view>
</view>
<view class="price">
价格:{{ infoList.total_pay_price }}
</view>
</view>
<!-- 温馨提示 -->
<view class="my_prompt">
<view class="title">
温馨提示请遵医嘱服药处方{{lists.valid_hours}}小时有效
</view>
<image src="/static/group/img-yzf.png" mode="" v-if="status==2"></image>
</view>
<view v-if="notice_id" class="rotation" @click.stop="rotation"> </view>
<!-- 状态 -->
</view>
</template>
<script>
import {
rotationApi,
prescripDetail,
getUseWay
} from "@/api/all.js";
export default {
data() {
return {
statusName: {
0: '待审核',
1: '已通过',
2: '未通过',
3: '待使用',
4: '已使用',
5: '未使用',
6: '已失效',
7: '已初审'
},
// status:flase,
status: true,
// status: "", // 处方状态
status: "", // 处方状态
id: "",
infoList: [],
num: 1,
rpList: [],
order_id: "",
lists: [],
useWay: [],
notice_id: ''
}
},
onLoad(e) {
// console.log('12')
// console.log(e, "id");
this.id = e.id
this.notice_id = e.notice_id
this.getInfo()
getUseWay({
store_id: uni.getStorageSync('store_id') || 11001,
}).then((res) => {
if (res.errcode == 0) {
const useWay = ["无", ...(res.data.map(it => it.name))]
uni.setStorageSync('useWay', JSON.stringify(useWay))
this.useWay = useWay
}
})
},
methods: {
async rotation() {
rotationApi({
notice_id: this.notice_id,
store_id: uni.getStorageSync('store_id') || '11001'
}).then(res => {
uni.showToast({
title: '转方成功',
icon: 'success',
mask: true
})
setTimeout(() => {
uni.navigateBack({ delta: 1 })
}, 1500)
})
},
// 数量
getnum(obj) {
let list = obj
let num = 0
list.map((it) => {
num += it.number
})
return num
},
getInfo() {
prescripDetail({
store_id: uni.getStorageSync('store_id') || '11001',
prescription_id: this.id
}).then((res) => {
// console.log(res, 'deta');
if (res.errcode == 0) {
this.infoList = res.data.content
this.rpList = res.data.pharmacistInfo
this.lists = res.data
this.status = res.data.status
// console.log(this.rpList, 'info');
}
})
},
// getDrug(obj) {
// console.log(obj, 'obj')
// if (!obj) {
// return
// }
// let list = (obj.chinese.length > 0 ? obj.chinese : obj.west) || []
// let drugs = []
// // console.log(list);
// for (let i = 0; i < list.length; i++) {
// if (obj.chinese.length > 0) {
// for (let j = 0; j < JSON.parse(list[i].content).length; j++) {
// drugs.push(JSON.parse(list[i].content)[j]['name'])
// }
// } else {
// drugs.push(JSON.parse(list[i].content)['name'])
// }
// }
// return drugs.join()
// },
},
mounted() {
// this.getInfo()
}
}
</script>
<style lang="scss" scoped>
.container {
width: 750rpx;
min-height: 100vh;
max-height: 100%;
padding-bottom: env(safe-area-inset-bottom);
.bg {
min-height: calc(100vh - 600rpx);
}
.head {
.head_record_top {
width: 710rpx;
margin: 14rpx auto 0;
display: flex;
align-items: center;
justify-content: space-between;
.record {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.record_state {
width: 80rpx;
height: 42rpx;
line-height: 42rpx;
text-align: center;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
border: 1rpx solid #C4C7CC;
}
}
.head_record_time {
text-align: right;
width: 750rpx;
padding: 0 32rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.head_record {
display: flex;
align-items: center;
justify-content: center;
position: relative;
image {
width: 160rpx;
height: 150rpx;
position: absolute;
left: 40%;
top: -8rpx;
}
.record_yard {
text-align: center;
.yard {
font-size: 46rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
.state {
height: 44rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
}
}
}
.head_record_time {
text-align: right;
margin-top: 20rpx;
width: 750rpx;
padding-right: 32rpx;
height: 34rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
line-height: 28rpx;
}
}
// 信息
.my_info {
width: 710rpx;
margin: 4rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 4rpx 4rpx;
.info {
width: 710rpx;
display: flex;
align-items: center;
.info_item {
.names {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 0rpx 0 8rpx;
// flex-wrap: nowrap;
// display: -webkit-box;
// -webkit-line-clamp: 1;
// -webkit-box-orient: vertical;
// text-overflow: ellipsis;
// overflow: hidden;
}
}
.name {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 26rpx 0 6rpx;
}
}
}
}
}
// 药品
.my_medical {
width: 710rpx;
margin: 4rpx auto;
padding: 1rpx 10rpx;
.title {
font-size: 32rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
color: #31353D;
margin-bottom: 2rpx;
}
.item {
width: 710rpx;
padding: 1rpx 0;
.name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
._name {
display: flex;
justify-content: space-between;
margin: 2rpx 80rpx 2rpx 0;
.text {
margin-right: 16rpx;
display: flex;
flex-direction: column;
._abbr {
color: #A7ABB0;
font-size: 20rpx;
margin-top: 2rpx;
}
}
}
}
.details {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
display: flex;
flex-direction: column;
justify-content: space-around;
.text {
text {
word-spacing: 1rpx;
}
}
}
}
.items {
width: 710rpx;
padding: 1rpx 0;
.name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
display: flex;
// justify-content: space-between;
flex-wrap: wrap;
._name {
display: flex;
justify-content: space-between;
margin: 2rpx 80rpx 2rpx 0;
.text {
margin-right: 26rpx;
display: flex;
flex-direction: column;
._abbr {
color: #A7ABB0;
font-size: 20rpx;
margin-top: 2rpx;
}
}
}
}
.details {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
display: flex;
flex-direction: column;
justify-content: space-around;
.text {
text {
word-spacing: 1rpx;
}
}
}
}
}
// 医嘱
.doctor_order {
width: 710rpx;
margin: 2rpx auto;
padding: 0 10rpx;
word-spacing: 2rpx;
.yz {
display: flex;
width: 100%;
color: #6C7380;
.order_info_left {
margin-right: 3rpx;
}
.order_info {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
line-height: 40rpx;
margin-bottom: 2rpx;
.info {
flex: 1;
}
}
}
.titles {
width: 710rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
margin: 8rpx 0 20rpx;
}
}
// 医生
.my_doctor {
width: 710rpx;
margin: 2rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
padding: 2rpx 4rpx;
.doctor_info {
width: 710rpx;
display: flex;
align-items: center;
margin-bottom: 4rpx;
flex-wrap: wrap;
.info {
min-width: 222rpx;
max-width: 400rpx;
margin: 5rpx 0 8rpx;
text {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
image {
width: 82rpx;
height: 62rpx;
vertical-align: middle;
}
.name {
width: 106rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
border-bottom: 2rpx solid #000000;
margin-left: 16rpx;
}
}
}
.price {
width: 710rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
}
// 温馨提示
.my_prompt {
width: 710rpx;
height: 304rpx;
margin: 0 auto;
padding: 0 4rpx;
position: relative;
.title {
width: 710rpx;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
line-height: 40rpx;
margin: 32rpx 0;
}
image {
width: 159rpx;
height: 100rpx;
position: absolute;
left: 60%;
top: 0%;
}
.prompt {
width: 710rpx;
height: 200rpx;
text {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
}
ol {
padding: 0 0 0 32rpx;
li {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
margin: 4rpx 0;
}
}
}
}
// 状态
.my_state {
width: 710rpx;
height: 40rpx;
margin: 12rpx auto;
padding-bottom: 110rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #F44336;
line-height: 40rpx;
}
.line {
border-bottom: 2rpx solid #31353D;
}
}
.rotation {
position: fixed;
bottom: 30rpx;
right: 24rpx;
left: 24rpx;
height: 80rpx;
display: flex;
font-size: 30rpx;
align-items: center;
justify-content: center;
background: #5792f9;
color: #fff;
border-radius: 12rpx;
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<view class="">
<view class="box safe-area-inset-bottom">
<view class="p-row-2 m-t-2">
<block v-if="list.length > 0">
<view class="p-32 b-r-8 white m-b-2 item" v-for="(item, i) in list" :key="item.id"
@click="$go('./prescriptionDetail?id=' +item.data + '&notice_id=' + item.id)">
<view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<u-icon margin-left="8" label-size="32" label-color="#6C7380"
:name="require('../../static/image/ys.png')" :label="item.content"
color="#6ACDBB" size="32"></u-icon>
<!-- <d-text :text="statusName[item.status]"></d-text> -->
</view>
<!-- <view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<d-text className="content-c fs-32" :text="getInfo(item.patient || {})"></d-text>
</view> -->
<view class="flex-row flex-ali-center flex-jus-sp m-t-2">
<d-text :text="item.notice_at"></d-text>
</view>
<view class="rotation" @click.stop="rotation(item)">转方</view>
</view>
</block>
</view>
</view>
</view>
</template>
<script>
import {
getNotice,
rotationApi
} from "@/api/all.js";
export default {
data() {
return {
list: [],
page: 1,
loading: true,
loading2: true,
status: "loading",
totalPage: 1,
loadText: {
loadmore: "轻轻上拉",
loading: "努力加载中",
nomore: "已加载全部",
},
current: 0,
status: ""
};
},
created() {},
onShow() {
this.getList();
},
methods: {
getInfo(user) {
return `${user.name} ${user.sex == 1 ? "男" : "女"} ${user.age}`;
},
async getList() {
getNotice({
page_size: 9999,
store_id: uni.getStorageSync('store_id') || '11001'
}).then(res => {
console.log(res, 'reess')
this.list = res.data.list
})
},
async rotation(row) {
rotationApi({
notice_id: row.id,
store_id: uni.getStorageSync('store_id') || '11001'
}).then(res => {
uni.showToast({
title: '转方成功',
icon: 'success',
mask: true
})
this.list = this.list.filter(item => item.id != row.id)
})
},
},
};
</script>
<style>
page {
background-color: #f9fafb;
}
</style>
<style lang="scss" scoped>
.tabs{
background-color: #fff;
width: 750rpx;
position: sticky;
top: 0rpx;
z-index: 9999999;
padding-top: 12rpx;
}
.box {
position: relative;
z-index: 982;
image {
width: 32rpx;
height: 32rpx;
}
}
::v-deep .box {
.u-tab-item {
line-height: 1 !important;
}
}
.state {
margin-top: 160rpx;
image {
width: 300rpx;
height: 223rpx;
}
}
.search {
padding: 16rpx 32rpx;
}
.item {
position: relative;
.rotation {
position: absolute;
right: 24rpx;
bottom: 24rpx;
padding: 8rpx 30rpx;
background: #5792f9;
color: #fff;
border-radius: 8rpx;
}
}
</style>

136
pages1.json Normal file
View File

@@ -0,0 +1,136 @@
{
"easycom": {
"autoscan": true,
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"^d-(.*)": "@/components/d-$1/d-$1.vue"
},
"pages": [{
"path": "pages/workbench/index",
"style": {
"navigationBarTitleText": "工作台",
"navigationStyle": "custom"
}
},
{
"path": "pages/patient/index",
"style": {
"navigationBarTitleText": "患者",
"navigationStyle": "custom"
}
},
{
"path": "pages/article/index",
"style": {
"navigationBarTitleText": "我的文章",
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
},
{
"path": "pages/my/index",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
},
{
"path": "pages/pharmacist/index",
"style": {
"navigationBarTitleText": "药师",
"navigationStyle": "custom"
}
}
],
"subPackages": [{
"root": "subPackages/sub_workbench",
"pages": [{
"path": "workbench_code",
"style": {
"navigationBarTitleText": "医生码"
}
}]
}, {
"root": "subPackages/sub_patient",
"pages": [{
"path": "patient_label",
"style": {
"navigationBarTitleText": "标签管理"
}
},
{
"path": "patient_massInfo",
"style": {
"navigationBarTitleText": "群发信息"
}
},
{
"path": "patient_select",
"style": {
"navigationBarTitleText": "群发信息"
}
},
{
"path": "patient_details",
"style": {
"navigationBarTitleText": "患者详情"
}
}
]
},
{
"root": "subPackages/sub_article",
"pages": [{
"path": "article_release",
"style": {
"navigationBarTitleText": "无标题文档"
}
}, {
"path": "article_draft",
"style": {
"navigationBarTitleText": "我的草稿"
}
}, {
"path": "article_video",
"style": {
"navigationBarTitleText": "无标题文档"
}
}
]
}
],
"tabBar": {
"color": "#fff",
"selectedColor": "#fff",
"backgroundColor": "#fff",
"borderStyle": "white",
"list": [{
"pagePath": "pages/workbench/index",
"text": "工作台"
},
{
"pagePath": "pages/patient/index",
"text": "患者"
},
{
"pagePath": "pages/article/index",
"text": "我的文章"
},
{
"pagePath": "pages/my/index",
"text": "我的"
},
{
"pagePath": "pages/pharmacist/index",
"text": "审方"
}
]
},
"globalStyle": {
"navigationBarTitleText": "掌上互联网医院",
"navigationBarBackgroundColor": "#fff",
"navigationBarTextStyle": "black",
"backgroundColor": "#f7f8fa"
}
}

BIN
static/group/img-yzf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
static/group/new_xx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
static/group/xkyard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
static/image/act.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

BIN
static/image/activate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
static/image/aq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
static/image/cf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
static/image/cf1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/image/cfd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
static/image/del2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

BIN
static/image/djr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
static/image/doctor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
static/image/dx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
static/image/dy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
static/image/fbwz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
static/image/fswz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
static/image/fwsz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
static/image/fz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

BIN
static/image/gou.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
static/image/grzl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/image/gzt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/image/hlwyy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/image/home1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
static/image/home2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
static/image/huiua.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
static/image/hz-qfxx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/image/hz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
static/image/hz_ddjz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
static/image/hz_xsjz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
static/image/hzxq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
static/image/i.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

BIN
static/image/i1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
static/image/i3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Some files were not shown because too many files have changed in this diff Show More