feat: 优化了若干接口,可用性存疑
This commit is contained in:
@@ -25,6 +25,18 @@ export function createBusinessApi(prefix, headerFlag) {
|
||||
getMyInfo() {
|
||||
return bizRequest({ url: `${prefix}-auth/my-info`, method: 'GET' });
|
||||
},
|
||||
/** 个人中心 - 我的资料(xk-api GET {prefix}-auth/my-profile,实时库数据) */
|
||||
getMyProfile() {
|
||||
return bizRequest({ url: `${prefix}-auth/my-profile`, method: 'GET' });
|
||||
},
|
||||
/** 个人中心 - 修改资料(仅昵称/头像,xk-api POST {prefix}-auth/update-my-profile) */
|
||||
updateMyProfile(data) {
|
||||
return bizRequest({ url: `${prefix}-auth/update-my-profile`, method: 'POST', data });
|
||||
},
|
||||
/** 个人中心 - 修改登录密码(xk-api POST {prefix}-auth/update-password) */
|
||||
updateMyPassword(data) {
|
||||
return bizRequest({ url: `${prefix}-auth/update-password`, method: 'POST', data });
|
||||
},
|
||||
getInputStats() {
|
||||
return bizRequest({ url: `${prefix}-auth/input-stats`, method: 'GET' });
|
||||
},
|
||||
|
||||
@@ -51,10 +51,65 @@ export function getClinicAdminMyInfo() {
|
||||
return clinicRequest({ url: `${PREFIX}-auth/my-info`, method: 'GET' });
|
||||
}
|
||||
|
||||
/** 个人中心 - 我的资料(xk-api GET clinic-admin-auth/my-profile,实时库数据) */
|
||||
export function getClinicAdminMyProfile() {
|
||||
return clinicRequest({ url: `${PREFIX}-auth/my-profile`, method: 'GET' });
|
||||
}
|
||||
|
||||
/** 个人中心 - 修改资料(仅昵称/头像,xk-api POST clinic-admin-auth/update-my-profile) */
|
||||
export function updateClinicAdminMyProfile(data) {
|
||||
return clinicRequest({ url: `${PREFIX}-auth/update-my-profile`, method: 'POST', data });
|
||||
}
|
||||
|
||||
/** 个人中心 - 修改登录密码(xk-api POST clinic-admin-auth/update-password) */
|
||||
export function updateClinicAdminMyPassword(data) {
|
||||
return clinicRequest({ url: `${PREFIX}-auth/update-password`, method: 'POST', data });
|
||||
}
|
||||
|
||||
export function getClinicAdminStoreQr() {
|
||||
return clinicRequest({ url: `${PREFIX}-auth/store-qr`, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作台看板统计(今日/本月订单、待发货、待审核提现、下单会员数)
|
||||
* xk-api GET /clinic-admin-dashboard/stats
|
||||
*/
|
||||
export function getClinicAdminDashboardStats() {
|
||||
return clinicRequest({ url: `${PREFIX}-dashboard/stats`, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作台功能入口下发(后台按角色分配的宫格入口,替代前端硬编码)
|
||||
* xk-api GET /clinic-admin-dashboard/entries
|
||||
*/
|
||||
export function getClinicAdminWorkbenchEntries() {
|
||||
return clinicRequest({ url: `${PREFIX}-dashboard/entries`, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能页用户数据:我的常用 + 最近使用
|
||||
* xk-api GET /clinic-admin-dashboard/entry-user-data
|
||||
*/
|
||||
export function getClinicAdminEntryUserData() {
|
||||
return clinicRequest({ url: `${PREFIX}-dashboard/entry-user-data`, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏功能入口(功能页长按触发),返回 { is_favorite }
|
||||
* xk-api POST /clinic-admin-dashboard/toggle-favorite-entry
|
||||
*/
|
||||
export function toggleClinicAdminFavoriteEntry(code) {
|
||||
return clinicRequest({ url: `${PREFIX}-dashboard/toggle-favorite-entry`, method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报功能入口使用(点击跳转时静默调用,调用方无需处理失败)
|
||||
* xk-api POST /clinic-admin-dashboard/report-entry-usage
|
||||
*/
|
||||
export function reportClinicAdminEntryUsage(code) {
|
||||
return clinicRequest({ url: `${PREFIX}-dashboard/report-entry-usage`, method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 本门店 VIP 开通记录列表
|
||||
* xk-api GET /clinic-admin-vip/list
|
||||
|
||||
@@ -22,6 +22,21 @@ export function getClinicSalespersonMyInfo() {
|
||||
return clinicSalespersonRequest({ url: `${AUTH_PREFIX}/my-info`, method: 'GET' });
|
||||
}
|
||||
|
||||
/** 个人中心 - 我的资料(xk-api GET clinic-salesperson-auth/my-profile,实时库数据) */
|
||||
export function getClinicSalespersonMyProfile() {
|
||||
return clinicSalespersonRequest({ url: `${AUTH_PREFIX}/my-profile`, method: 'GET' });
|
||||
}
|
||||
|
||||
/** 个人中心 - 修改资料(仅昵称/头像,xk-api POST clinic-salesperson-auth/update-my-profile) */
|
||||
export function updateClinicSalespersonMyProfile(data) {
|
||||
return clinicSalespersonRequest({ url: `${AUTH_PREFIX}/update-my-profile`, method: 'POST', data });
|
||||
}
|
||||
|
||||
/** 个人中心 - 修改登录密码(xk-api POST clinic-salesperson-auth/update-password) */
|
||||
export function updateClinicSalespersonMyPassword(data) {
|
||||
return clinicSalespersonRequest({ url: `${AUTH_PREFIX}/update-password`, method: 'POST', data });
|
||||
}
|
||||
|
||||
export function getClinicSalespersonCommissionList(params) {
|
||||
return clinicSalespersonRequest({ url: `${PREFIX}/commission-list`, method: 'GET', data: params });
|
||||
}
|
||||
@@ -74,3 +89,35 @@ export function getClinicSalespersonTransferPrescriptionDetail(id) {
|
||||
export function getClinicSalespersonInvitedList(params) {
|
||||
return clinicSalespersonRequest({ url: `${PREFIX}/invited-salesperson-list`, method: 'GET', data: params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广员工作台功能入口下发(后台按角色分配的宫格入口,替代前端硬编码)
|
||||
* xk-api GET /clinic-salesperson-dashboard/entries
|
||||
*/
|
||||
export function getClinicSalespersonWorkbenchEntries() {
|
||||
return clinicSalespersonRequest({ url: '/newApi/clinic-salesperson-dashboard/entries', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能页用户数据:我的常用 + 最近使用
|
||||
* xk-api GET /clinic-salesperson-dashboard/entry-user-data
|
||||
*/
|
||||
export function getClinicSalespersonEntryUserData() {
|
||||
return clinicSalespersonRequest({ url: '/newApi/clinic-salesperson-dashboard/entry-user-data', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏功能入口(功能页长按触发),返回 { is_favorite }
|
||||
* xk-api POST /clinic-salesperson-dashboard/toggle-favorite-entry
|
||||
*/
|
||||
export function toggleClinicSalespersonFavoriteEntry(code) {
|
||||
return clinicSalespersonRequest({ url: '/newApi/clinic-salesperson-dashboard/toggle-favorite-entry', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报功能入口使用(点击跳转时静默调用,调用方无需处理失败)
|
||||
* xk-api POST /clinic-salesperson-dashboard/report-entry-usage
|
||||
*/
|
||||
export function reportClinicSalespersonEntryUsage(code) {
|
||||
return clinicSalespersonRequest({ url: '/newApi/clinic-salesperson-dashboard/report-entry-usage', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
54
api/pharmacistExamine.js
Normal file
54
api/pharmacistExamine.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 药师审方相关接口(xk-api,取值用 code / result / message,配合 unwrapXkApi)
|
||||
*
|
||||
* 老 Yii service/v1/examine/* 已迁移到 xk-api 的 /pharmacist-examine/*,
|
||||
* 鉴权复用医生端登录 token(ServiceUser),后端中间件强校验药师角色(role=2)
|
||||
*/
|
||||
import {
|
||||
req
|
||||
} from '@/common/js/index.js';
|
||||
|
||||
// xk-api GET /pharmacist-examine/list 审方列表(status: 0待审核/1已通过/2未通过;分页 page/pageSize)
|
||||
export function getExamineList(data) {
|
||||
return req.request({
|
||||
url: '/newApi/pharmacist-examine/list',
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// xk-api GET /pharmacist-examine/detail 审方详情(按处方号)
|
||||
export function getExamineDetail(data) {
|
||||
return req.request({
|
||||
url: '/newApi/pharmacist-examine/detail',
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// xk-api POST /pharmacist-examine/verify 审方通过/驳回(status=1 通过;status=2 驳回需 reject_reason)
|
||||
export function verifyExamine(data) {
|
||||
return req.request({
|
||||
url: '/newApi/pharmacist-examine/verify',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// xk-api GET /pharmacist-examine/source 处方溯源(按处方号)
|
||||
export function getExamineSource(data) {
|
||||
return req.request({
|
||||
url: '/newApi/pharmacist-examine/source',
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// xk-api GET /pharmacist-examine/use-way 中药用法(煎法)字典
|
||||
export function getExamineUseWay(data) {
|
||||
return req.request({
|
||||
url: '/newApi/pharmacist-examine/use-way',
|
||||
method: 'GET',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,63 @@
|
||||
import { req } from '@/common/js/index.js';
|
||||
import { platformAdminApi } from '@/api/businessApi.js';
|
||||
import { unwrapClinicRes } from '@/api/clinicAdmin.js';
|
||||
|
||||
export { platformAdminApi };
|
||||
export const getPlatformAdminMyInfo = () => platformAdminApi.getMyInfo();
|
||||
|
||||
/**
|
||||
* 平台工作台专属请求助手(带 _platformAdmin 头 + 统一 { res, data, ok } 返回)
|
||||
* 单独封装(不进 createBusinessApi):dashboard 系列接口仅平台端有,业务员端无此路由
|
||||
*/
|
||||
function platformDashboardRequest(options) {
|
||||
return req.request({
|
||||
...options,
|
||||
header: { ...(options.header || {}), _platformAdmin: '1' },
|
||||
}).then((res) => {
|
||||
const data = unwrapClinicRes(res);
|
||||
const ok = !!(res && Number(res.code) === 0);
|
||||
return { res, data, ok };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台工作台看板统计(待审核门店/医生/提现数 + 今日订单概览)
|
||||
* xk-api GET /platform-admin-dashboard/stats
|
||||
*/
|
||||
export function getPlatformAdminDashboardStats() {
|
||||
return platformDashboardRequest({ url: '/newApi/platform-admin-dashboard/stats', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台工作台功能入口下发(后台按角色分配的宫格入口,替代前端硬编码)
|
||||
* xk-api GET /platform-admin-dashboard/entries
|
||||
*/
|
||||
export function getPlatformAdminWorkbenchEntries() {
|
||||
return platformDashboardRequest({ url: '/newApi/platform-admin-dashboard/entries', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能页用户数据:我的常用 + 最近使用
|
||||
* xk-api GET /platform-admin-dashboard/entry-user-data
|
||||
*/
|
||||
export function getPlatformAdminEntryUserData() {
|
||||
return platformDashboardRequest({ url: '/newApi/platform-admin-dashboard/entry-user-data', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏功能入口(功能页长按触发),返回 { is_favorite }
|
||||
* xk-api POST /platform-admin-dashboard/toggle-favorite-entry
|
||||
*/
|
||||
export function togglePlatformAdminFavoriteEntry(code) {
|
||||
return platformDashboardRequest({ url: '/newApi/platform-admin-dashboard/toggle-favorite-entry', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报功能入口使用(点击跳转时静默调用,调用方无需处理失败)
|
||||
* xk-api POST /platform-admin-dashboard/report-entry-usage
|
||||
*/
|
||||
export function reportPlatformAdminEntryUsage(code) {
|
||||
return platformDashboardRequest({ url: '/newApi/platform-admin-dashboard/report-entry-usage', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
export default platformAdminApi;
|
||||
|
||||
@@ -1,5 +1,55 @@
|
||||
import { req } from '@/common/js/index.js';
|
||||
import { salespersonApi } from '@/api/businessApi.js';
|
||||
import { unwrapClinicRes } from '@/api/clinicAdmin.js';
|
||||
|
||||
export const getSalespersonMyInfo = salespersonApi.getMyInfo.bind(salespersonApi);
|
||||
export const getSalespersonInputStats = salespersonApi.getInputStats.bind(salespersonApi);
|
||||
|
||||
/**
|
||||
* 业务员工作台专属请求助手(带 _salesperson 头 + 统一 { res, data, ok } 返回)
|
||||
* 单独封装(不进 createBusinessApi):dashboard 系列接口仅业务员端有此路由
|
||||
*/
|
||||
function salespersonDashboardRequest(options) {
|
||||
return req.request({
|
||||
...options,
|
||||
header: { ...(options.header || {}), _salesperson: '1' },
|
||||
}).then((res) => {
|
||||
const data = unwrapClinicRes(res);
|
||||
const ok = !!(res && Number(res.code) === 0);
|
||||
return { res, data, ok };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务员工作台功能入口下发(后台按角色分配的宫格入口,替代前端硬编码)
|
||||
* xk-api GET /salesperson-dashboard/entries
|
||||
*/
|
||||
export function getSalespersonWorkbenchEntries() {
|
||||
return salespersonDashboardRequest({ url: '/newApi/salesperson-dashboard/entries', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能页用户数据:我的常用 + 最近使用
|
||||
* xk-api GET /salesperson-dashboard/entry-user-data
|
||||
*/
|
||||
export function getSalespersonEntryUserData() {
|
||||
return salespersonDashboardRequest({ url: '/newApi/salesperson-dashboard/entry-user-data', method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏功能入口(功能页长按触发),返回 { is_favorite }
|
||||
* xk-api POST /salesperson-dashboard/toggle-favorite-entry
|
||||
*/
|
||||
export function toggleSalespersonFavoriteEntry(code) {
|
||||
return salespersonDashboardRequest({ url: '/newApi/salesperson-dashboard/toggle-favorite-entry', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报功能入口使用(点击跳转时静默调用,调用方无需处理失败)
|
||||
* xk-api POST /salesperson-dashboard/report-entry-usage
|
||||
*/
|
||||
export function reportSalespersonEntryUsage(code) {
|
||||
return salespersonDashboardRequest({ url: '/newApi/salesperson-dashboard/report-entry-usage', method: 'POST', data: { code } });
|
||||
}
|
||||
|
||||
export default salespersonApi;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { req } from '@/common/js/index.js';
|
||||
const SALESPERSON_PREFIX = '/newApi/salesperson-upload/';
|
||||
const PLATFORM_PREFIX = '/newApi/platform-admin-upload/';
|
||||
const CLINIC_ADMIN_PREFIX = '/newApi/clinic-admin-upload/';
|
||||
const CLINIC_SALESPERSON_PREFIX = '/newApi/clinic-salesperson-upload/';
|
||||
|
||||
function uploadChatFileByPrefix(prefix, headerKey, params) {
|
||||
return req.request({
|
||||
@@ -102,6 +103,52 @@ export function uploadSalespersonManageFileApi(params) {
|
||||
return uploadClinicAdminChatFileApi(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人中心头像上传(xk-api POST {end}-upload/image,UploadController::image)
|
||||
* 按当前 loginMode 选择四端各自的上传路由与请求头标识;
|
||||
* uni.uploadFile 返回的 body 是字符串,这里手动 JSON.parse 后按 jok 形态(code/result.url)解包
|
||||
* @param {string} filePath 本地临时文件路径
|
||||
* @returns {Promise<{ok: boolean, url: string|null, message?: string}>}
|
||||
*/
|
||||
export function uploadProfileAvatarApi(filePath) {
|
||||
const mode = uni.getStorageSync('loginMode');
|
||||
let prefix = CLINIC_ADMIN_PREFIX;
|
||||
let headerKey = '_clinicAdmin';
|
||||
if (mode === 'platform_admin') {
|
||||
prefix = PLATFORM_PREFIX;
|
||||
headerKey = '_platformAdmin';
|
||||
} else if (mode === 'salesperson') {
|
||||
prefix = SALESPERSON_PREFIX;
|
||||
headerKey = '_salesperson';
|
||||
} else if (mode === 'clinic_salesperson') {
|
||||
prefix = CLINIC_SALESPERSON_PREFIX;
|
||||
headerKey = '_clinicSalesperson';
|
||||
}
|
||||
return req.request({
|
||||
url: `${prefix}image`,
|
||||
type: 'upload',
|
||||
method: 'POST',
|
||||
filePath,
|
||||
name: 'file',
|
||||
formData: {},
|
||||
header: { [headerKey]: '1' },
|
||||
}).then((res) => {
|
||||
let body = res;
|
||||
if (typeof body === 'string') {
|
||||
try {
|
||||
body = JSON.parse(body);
|
||||
} catch (e) {
|
||||
body = null;
|
||||
}
|
||||
}
|
||||
// 该接口是 xk-api:成功 code=0,业务数据在 result
|
||||
if (body && Number(body.code) === 0 && body.result && body.result.url) {
|
||||
return { ok: true, url: body.result.url };
|
||||
}
|
||||
return { ok: false, url: null, message: (body && body.message) || '上传失败' };
|
||||
});
|
||||
}
|
||||
|
||||
function unwrapUploadResult(res) {
|
||||
if (res == null) return null;
|
||||
if (res.result !== undefined && res.result !== null) return res.result;
|
||||
|
||||
196
components/admin-my-panel/admin-my-panel.vue
Normal file
196
components/admin-my-panel/admin-my-panel.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view class="admin-my-panel">
|
||||
<!-- 资料区:有图片头像显示图片,否则文字首字圆(四端统一形态) -->
|
||||
<view class="profile-panel" hover-class="profile-hover" :hover-stay-time="100" @tap="goProfile">
|
||||
<image v-if="avatar" class="avatar-img" :src="avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ displayName }}</text>
|
||||
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
||||
<text v-if="storeName && storeName !== '-'" class="meta-line">{{ storeName }}</text>
|
||||
<text v-if="phone" class="meta-line">{{ phone }}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||
</view>
|
||||
<!-- 诊所管理员端在这里插 VIP 会员卡 -->
|
||||
<slot name="after-profile"></slot>
|
||||
<!-- 白色菜单卡:个人中心入口(第一项)+ 绑定微信 + 切换账号 + 工作台卡片样式 -->
|
||||
<view class="setting-panel">
|
||||
<view class="entry-row" hover-class="entry-row-hover" :hover-stay-time="100" @tap="goProfile">
|
||||
<text class="entry-label">个人中心</text>
|
||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||
</view>
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
<account-switch-entry />
|
||||
<card-pref-entry />
|
||||
</view>
|
||||
<!-- 独立绿色退出按钮(四端逻辑完全一致,收进组件) -->
|
||||
<view class="logout-wrap">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||
@click="logout"
|
||||
>退出登录</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* admin-my-panel 四个管理端「我的」页共享面板
|
||||
* 为什么做成组件:诊所管理员/平台管理员/业务员/诊所推广员四端「我的」页高度同构
|
||||
* (资料区 + 菜单卡 + 退出按钮),抽组件统一形态与逻辑,避免四份复制粘贴;
|
||||
* 各端差异(VIP 卡、门店名、手机号)通过 props / slot 传入
|
||||
*/
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue';
|
||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue';
|
||||
import CardPrefEntry from '@/components/workbench-card-pref/card-pref-entry.vue';
|
||||
import { clearLoginStorage } from '@/utils/loginSession.js';
|
||||
|
||||
export default {
|
||||
name: 'AdminMyPanel',
|
||||
components: { AccountSwitchEntry, WxBindEntry, CardPrefEntry },
|
||||
props: {
|
||||
/** 展示姓名(各端从缓存/接口取好后传入) */
|
||||
displayName: { type: String, default: '' },
|
||||
/** 角色徽标文案(诊所管理员/平台管理员/业务员/推广员) */
|
||||
roleText: { type: String, default: '' },
|
||||
/** 图片头像地址,为空时降级为文字首字圆 */
|
||||
avatar: { type: String, default: '' },
|
||||
/** 副行:所属门店名(可选) */
|
||||
storeName: { type: String, default: '' },
|
||||
/** 副行:手机号(可选,推广员端展示) */
|
||||
phone: { type: String, default: '' },
|
||||
/** 无姓名时文字头像的兜底字 */
|
||||
fallbackChar: { type: String, default: '管' },
|
||||
},
|
||||
computed: {
|
||||
/** 文字头像取姓名首字(模板禁止方法调用,统一在 computed 里算好) */
|
||||
avatarText() {
|
||||
return (this.displayName || this.fallbackChar).slice(0, 1);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 供页面 onShow 调用:刷新微信绑定状态 */
|
||||
refreshWxBind() {
|
||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh();
|
||||
},
|
||||
/** 进入共享个人中心页(sub_business_shared 分包页面可被任意分包 navigateTo) */
|
||||
goProfile() {
|
||||
uni.navigateTo({ url: '/subPackages/sub_business_shared/profile/index' });
|
||||
},
|
||||
/** 退出登录:确认弹窗 → 清全部登录态 → 回登录页(四端一致) */
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定退出登录?',
|
||||
confirmColor: '#6ACDBB',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return;
|
||||
clearLoginStorage();
|
||||
uni.reLaunch({ url: '/pages/login/index' });
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$theme-dark: #4db6a8;
|
||||
|
||||
.admin-my-panel {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 60rpx;
|
||||
background: #f5f7f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 资料区整块可点进入个人中心 */
|
||||
.profile-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
padding: 60rpx 40rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.profile-hover {
|
||||
background: #fafbfc;
|
||||
}
|
||||
.avatar-img {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
background: #f2f3f5;
|
||||
}
|
||||
.avatar-circle {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
||||
color: #fff;
|
||||
font-size: 52rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.user-name {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #222b2a;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.role-badge {
|
||||
align-self: flex-start;
|
||||
background: rgba(106, 205, 187, 0.1);
|
||||
border: 1rpx solid rgba(106, 205, 187, 0.2);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.role-text {
|
||||
font-size: 22rpx;
|
||||
color: $theme-dark;
|
||||
font-weight: 600;
|
||||
}
|
||||
.meta-line {
|
||||
font-size: 26rpx;
|
||||
color: #86909c;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.setting-panel {
|
||||
background: #fff;
|
||||
padding: 8rpx 40rpx 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
/* 「个人中心」入口行:与 account-switch-entry / card-pref-entry 行规格一致 */
|
||||
.entry-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 36rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
}
|
||||
.entry-row-hover {
|
||||
background: #f7f8fa;
|
||||
}
|
||||
.entry-label {
|
||||
font-size: 30rpx;
|
||||
color: #31353d;
|
||||
}
|
||||
.logout-wrap {
|
||||
padding: 0 40rpx 32rpx;
|
||||
}
|
||||
</style>
|
||||
99
components/workbench-card-pref/card-pref-entry.vue
Normal file
99
components/workbench-card-pref/card-pref-entry.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<!--
|
||||
工作台卡片样式入口行(四个业务角色「我的」页共用,放 setting-panel 内)
|
||||
由旧版 u-switch 开关改为导航行:显示当前样式文案 + chevron,点击进入图形化单选页
|
||||
-->
|
||||
<view class="switch-row" hover-class="row-hover" @tap="goStylePage">
|
||||
<view class="pref-text">
|
||||
<text class="switch-label">工作台卡片样式</text>
|
||||
<text class="pref-desc">长卡片 / 宫格两种形态可选</text>
|
||||
</view>
|
||||
<view class="pref-value">
|
||||
<text class="value-text">{{ styleLabel }}</text>
|
||||
<u-icon name="arrow-right" size="24" color="#C0C4CC"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* card-pref-entry 工作台卡片样式入口
|
||||
* 为什么做成组件:四个业务端「我的」页都需要同一入口,抽组件避免复制粘贴;
|
||||
* 偏好存本地 storage(wb_card_style),选择页写入后 uni.$emit 广播,本组件监听同步文案,
|
||||
* 各工作台首页 onShow 时读取生效
|
||||
*/
|
||||
import { getCardStyle } from '@/utils/workbenchCardPref.js';
|
||||
|
||||
export default {
|
||||
name: 'CardPrefEntry',
|
||||
data() {
|
||||
return {
|
||||
/** 当前样式:long 长卡片 / short 宫格 */
|
||||
style: 'short',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
styleLabel() {
|
||||
return this.style === 'long' ? '长卡片' : '宫格';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.style = getCardStyle();
|
||||
// 选择页切换后广播同步(组件拿不到页面 onShow,用事件总线保证返回时文案已更新)
|
||||
uni.$on('wbCardStyleChanged', this.onStyleChanged);
|
||||
},
|
||||
beforeDestroy() {
|
||||
uni.$off('wbCardStyleChanged', this.onStyleChanged);
|
||||
},
|
||||
methods: {
|
||||
onStyleChanged(style) {
|
||||
this.style = style === 'long' ? 'long' : 'short';
|
||||
},
|
||||
/** 进入图形化单选页(共享分包页面,四端均可 navigateTo) */
|
||||
goStylePage() {
|
||||
uni.navigateTo({ url: '/subPackages/sub_business_shared/card-style/index' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.switch-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 36rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
}
|
||||
|
||||
.row-hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.pref-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.switch-label {
|
||||
font-size: 30rpx;
|
||||
color: #31353d;
|
||||
}
|
||||
|
||||
.pref-desc {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.pref-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.value-text {
|
||||
font-size: 26rpx;
|
||||
color: #86909c;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
</style>
|
||||
432
components/workbench-features/workbench-features.vue
Normal file
432
components/workbench-features/workbench-features.vue
Normal file
@@ -0,0 +1,432 @@
|
||||
<template>
|
||||
<!--
|
||||
功能中心页共享组件(诊所管理员/平台管理员/业务员/诊所推广员四端「功能」tab 复用)
|
||||
结构:顶部搜索框 + u-tabs/swiper 联动(常用 | 全部 | 各分组),金刚区宫格展示入口
|
||||
交互:点击入口跳转并静默上报使用;长按入口收藏/取消收藏到「我的常用」;搜索本地过滤
|
||||
放主包 components/ 原因:四端页面分属不同分包,微信不允许跨分包引用组件(同 xk-service-card)
|
||||
-->
|
||||
<view class="wf-wrap" :style="{ height: wrapHeight }">
|
||||
<!-- 搜索框:本地过滤当前角色全部入口,输入即出结果 -->
|
||||
<view class="wf-search">
|
||||
<u-search
|
||||
v-model="keyword"
|
||||
placeholder="搜索功能"
|
||||
shape="round"
|
||||
bg-color="#FFFFFF"
|
||||
:show-action="false"
|
||||
:clearabled="true"
|
||||
></u-search>
|
||||
</view>
|
||||
|
||||
<!-- 加载/失败/空配置三态:占满内容区居中展示 -->
|
||||
<view v-if="loading" class="wf-center">
|
||||
<text class="wf-center-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="failed" class="wf-center">
|
||||
<text class="wf-center-text">功能列表加载失败</text>
|
||||
<view class="wf-retry" hover-class="wf-retry-hover" @tap="reload">
|
||||
<text>重新加载</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="sections.length === 0" class="wf-center">
|
||||
<text class="wf-center-text">暂无可用功能,请联系管理员配置</text>
|
||||
</view>
|
||||
|
||||
<!-- 搜索态:覆盖 tabs/swiper,展示扁平匹配结果 -->
|
||||
<scroll-view v-else-if="isSearching" scroll-y class="wf-search-scroll">
|
||||
<view class="wf-page">
|
||||
<view class="wf-panel" v-if="searchItems.length">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in searchItems" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wf-hint" v-else>
|
||||
<text>没有找到「{{ keyword }}」相关功能</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 常规态:tabs + swiper 双向联动(点 tab / 左右滑都走 switchTab) -->
|
||||
<block v-else>
|
||||
<view class="wf-tabs">
|
||||
<u-tabs
|
||||
:list="tabs"
|
||||
:is-scroll="tabs.length > 4"
|
||||
:current="current"
|
||||
:height="88"
|
||||
active-color="#6ACDBB"
|
||||
@change="switchTab"
|
||||
></u-tabs>
|
||||
</view>
|
||||
<swiper class="wf-swiper" :current="current" @change="onSwiperChange">
|
||||
<swiper-item v-for="(page, pi) in swiperPages" :key="pi">
|
||||
<scroll-view scroll-y class="wf-scroll">
|
||||
<!-- 常用 tab:我的常用 + 最近使用两个分区 -->
|
||||
<view v-if="page.type === 'fav'" class="wf-page">
|
||||
<view class="wf-group-title">我的常用</view>
|
||||
<view class="wf-panel" v-if="favItems.length">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in favItems" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wf-hint" v-else>
|
||||
<text>长按任意功能,可添加到我的常用</text>
|
||||
</view>
|
||||
<view class="wf-group-title">最近使用</view>
|
||||
<view class="wf-panel" v-if="recentItems.length">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in recentItems" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wf-hint" v-else>
|
||||
<text>暂无使用记录,点击功能后会出现在这里</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 全部 tab:所有分组一页看全(分组标题 + 各组金刚区) -->
|
||||
<view v-else-if="page.type === 'all'" class="wf-page">
|
||||
<view v-for="section in sections" :key="section.key">
|
||||
<view class="wf-group-title">{{ section.title }}</view>
|
||||
<view class="wf-panel">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in section.items" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 分组 tab:该组全部入口金刚区 -->
|
||||
<view v-else class="wf-page">
|
||||
<view class="wf-panel">
|
||||
<view class="wf-grid">
|
||||
<xk-service-card
|
||||
v-for="item in page.items" :key="item.key"
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label" :theme="item.theme"
|
||||
@click="onItemTap(item)" @longpress="onItemLongpress(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* workbench-features 功能中心共享组件
|
||||
*
|
||||
* 为什么做成组件而不是共享页面:四端「功能」tab 页需要套各自的 page-layout(navbar/tabbar 配置不同),
|
||||
* 页面壳由各端提供,数据与交互全部收敛到这里,避免四份复制。
|
||||
*
|
||||
* props.api 约定(由各端壳页注入对应角色的接口,全部返回 Promise<{ res, data, ok }>):
|
||||
* - getEntries() GET 当前角色启用入口(扁平数组)
|
||||
* - getUserData() GET { favorites: [], recents: [] }
|
||||
* - toggleFavorite(code) POST 收藏/取消收藏,data = { is_favorite }
|
||||
* - reportUsage(code) POST 使用上报(静默,失败不影响跳转)
|
||||
*
|
||||
* 事件:
|
||||
* - @special(item):点击了无 path 的特殊入口(如推广员端 cs_transfer 传方弹窗),由壳页按 code 处理
|
||||
*/
|
||||
import { groupEntriesToSections, normalizeEntry } from '@/utils/workbenchEntries.js';
|
||||
|
||||
export default {
|
||||
name: 'WorkbenchFeatures',
|
||||
props: {
|
||||
/** 角色接口集合:{ getEntries, getUserData, toggleFavorite, reportUsage } */
|
||||
api: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
failed: false,
|
||||
/** 分组入口(groupEntriesToSections 输出结构) */
|
||||
sections: [],
|
||||
/** 我的常用 / 最近使用(已归一化为渲染项) */
|
||||
favItems: [],
|
||||
recentItems: [],
|
||||
keyword: '',
|
||||
current: 0,
|
||||
/** 内容区高度(px):layout 的 navbar 与 tabbar 之间,swiper 需要确定高度才能滚动 */
|
||||
bodyHeight: 600,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
wrapHeight() {
|
||||
return this.bodyHeight + 'px';
|
||||
},
|
||||
isSearching() {
|
||||
return this.keyword.trim() !== '';
|
||||
},
|
||||
/** swiper 页序列:固定「常用」「全部」两页在前,其后每个分组一页 */
|
||||
swiperPages() {
|
||||
const pages = [
|
||||
{ key: '__fav__', type: 'fav', title: '常用', items: [] },
|
||||
{ key: '__all__', type: 'all', title: '全部', items: [] },
|
||||
];
|
||||
this.sections.forEach((s) => {
|
||||
pages.push({ key: s.key, type: 'group', title: s.title, items: s.items });
|
||||
});
|
||||
return pages;
|
||||
},
|
||||
/** u-tabs 数据源(name 字段) */
|
||||
tabs() {
|
||||
return this.swiperPages.map((p) => ({ name: p.title }));
|
||||
},
|
||||
/** 全部入口扁平列表(搜索用) */
|
||||
allItems() {
|
||||
const list = [];
|
||||
this.sections.forEach((s) => {
|
||||
s.items.forEach((item) => list.push(item));
|
||||
});
|
||||
return list;
|
||||
},
|
||||
/** 搜索结果:名称/描述包含关键词(大小写不敏感) */
|
||||
searchItems() {
|
||||
const kw = this.keyword.trim().toLowerCase();
|
||||
if (!kw) return [];
|
||||
return this.allItems.filter((item) => {
|
||||
const label = (item.label || '').toLowerCase();
|
||||
const desc = (item.desc || '').toLowerCase();
|
||||
return label.indexOf(kw) !== -1 || desc.indexOf(kw) !== -1;
|
||||
});
|
||||
},
|
||||
/** 已收藏 code 集合(长按菜单文案判断用) */
|
||||
favCodes() {
|
||||
return this.favItems.map((item) => item.key);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.calcHeight();
|
||||
this.reload();
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 计算内容区高度:窗口高度 - layout 顶部(状态栏+navbar)- layout 底部 tabbar 预留
|
||||
* 与各端 page-layout 的 bodyStyle 计算保持一致(paddingTop=状态栏+navbar px,paddingBottom=100rpx+安全区)
|
||||
*/
|
||||
calcHeight() {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
const rpx = sys.windowWidth / 750;
|
||||
const top = (this.$statusBarHeight || sys.statusBarHeight || 0) + (this.$navbarHeight || 44);
|
||||
let safeBottom = 0;
|
||||
if (sys.safeAreaInsets && sys.safeAreaInsets.bottom) {
|
||||
safeBottom = sys.safeAreaInsets.bottom;
|
||||
} else if (sys.safeArea && sys.screenHeight) {
|
||||
safeBottom = sys.screenHeight - sys.safeArea.bottom;
|
||||
}
|
||||
this.bodyHeight = Math.max(400, sys.windowHeight - top - 100 * rpx - safeBottom);
|
||||
},
|
||||
/** 全量加载:入口列表(必须)+ 用户数据(失败静默,常用/历史留空) */
|
||||
reload() {
|
||||
this.loading = true;
|
||||
this.failed = false;
|
||||
this.api.getEntries().then(({ data, ok }) => {
|
||||
if (ok && Array.isArray(data)) {
|
||||
this.sections = groupEntriesToSections(data);
|
||||
// 分组数变化时防止 current 越界(固定页为常用+全部两页,如后台调整了分组)
|
||||
if (this.current >= this.sections.length + 2) this.current = 0;
|
||||
} else {
|
||||
this.failed = true;
|
||||
}
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.failed = true;
|
||||
this.loading = false;
|
||||
});
|
||||
this.refreshUserData();
|
||||
},
|
||||
/** 刷新常用/最近使用(壳页 onShow 也会调用,保证跳转返回后历史及时更新) */
|
||||
refreshUserData() {
|
||||
if (!this.api.getUserData) return;
|
||||
this.api.getUserData().then(({ data, ok }) => {
|
||||
if (!ok || !data) return;
|
||||
this.favItems = this.normalizeList(data.favorites);
|
||||
this.recentItems = this.normalizeList(data.recents);
|
||||
}).catch(() => {
|
||||
// 用户数据是增强信息,失败静默(不打断功能页主流程)
|
||||
});
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与首页同一映射,字段完全一致) */
|
||||
normalizeList(list) {
|
||||
if (!Array.isArray(list)) return [];
|
||||
const items = [];
|
||||
list.forEach((entry) => {
|
||||
const item = normalizeEntry(entry);
|
||||
if (item) items.push(item);
|
||||
});
|
||||
return items;
|
||||
},
|
||||
/** tabs 与 swiper 统一切换入口 */
|
||||
switchTab(index) {
|
||||
if (this.current === index) return;
|
||||
this.current = index;
|
||||
},
|
||||
/** swiper 手势滑动回调:回写 current 让顶部 tab 高亮跟随 */
|
||||
onSwiperChange(e) {
|
||||
this.switchTab(e.detail.current);
|
||||
},
|
||||
/**
|
||||
* 点击入口:静默上报使用 → 跳转
|
||||
* 无 path 的特殊入口(如 cs_transfer 传方弹窗)抛给壳页按 code 处理
|
||||
*/
|
||||
onItemTap(item) {
|
||||
if (this.api.reportUsage) {
|
||||
// 上报是辅助行为:不等待、不处理失败,绝不阻塞跳转
|
||||
this.api.reportUsage(item.key).catch(() => {});
|
||||
}
|
||||
if (!item.path) {
|
||||
this.$emit('special', item);
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: item.path,
|
||||
fail: () => {
|
||||
// 目标是当前页栈不允许 navigateTo 的页面(如各端首页)时降级 reLaunch
|
||||
uni.reLaunch({ url: item.path });
|
||||
},
|
||||
});
|
||||
},
|
||||
/** 长按入口:弹收藏/取消收藏操作单 */
|
||||
onItemLongpress(item) {
|
||||
const isFav = this.favCodes.indexOf(item.key) !== -1;
|
||||
uni.showActionSheet({
|
||||
itemList: [isFav ? '从我的常用移除' : '添加到我的常用'],
|
||||
success: (r) => {
|
||||
if (r.tapIndex === 0) this.doToggleFavorite(item);
|
||||
},
|
||||
});
|
||||
},
|
||||
/** 收藏切换:成功后按后端返回状态提示并刷新常用列表 */
|
||||
doToggleFavorite(item) {
|
||||
if (!this.api.toggleFavorite) return;
|
||||
this.api.toggleFavorite(item.key).then(({ res, data, ok }) => {
|
||||
if (ok) {
|
||||
const added = !!(data && data.is_favorite);
|
||||
uni.showToast({ title: added ? '已添加到常用' : '已从常用移除', icon: 'none' });
|
||||
this.refreshUserData();
|
||||
} else {
|
||||
uni.showToast({ title: (res && res.message) || '操作失败,请稍后重试', icon: 'none' });
|
||||
}
|
||||
}).catch(() => {
|
||||
uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 纵向 flex:搜索/tabs 固定高度,swiper 吃满剩余空间(swiper 必须有确定高度才能滚动) */
|
||||
.wf-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f9fafb;
|
||||
}
|
||||
.wf-search {
|
||||
flex-shrink: 0;
|
||||
padding: 20rpx 24rpx 12rpx;
|
||||
}
|
||||
.wf-tabs {
|
||||
flex-shrink: 0;
|
||||
background: #FFFFFF;
|
||||
border-bottom: 1rpx solid rgba(60, 60, 67, 0.06);
|
||||
}
|
||||
.wf-swiper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.wf-scroll {
|
||||
height: 100%;
|
||||
}
|
||||
.wf-search-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
.wf-page {
|
||||
padding: 20rpx 24rpx 40rpx;
|
||||
}
|
||||
/* 分区标题:对齐首页 panel-title 规格但更轻量 */
|
||||
.wf-group-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
padding: 8rpx 6rpx 16rpx;
|
||||
}
|
||||
/* 金刚区白卡容器:iOS 卡片规格与首页/入口卡一致 */
|
||||
.wf-panel {
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 16rpx 12rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
/* 4 列金刚区:minmax(0,1fr) 防止长标签撑爆轨道 */
|
||||
.wf-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
row-gap: 8rpx;
|
||||
}
|
||||
/* 空态提示条:浅灰圆角块,弱引导 */
|
||||
.wf-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(60, 60, 67, 0.04);
|
||||
border-radius: 24rpx;
|
||||
padding: 36rpx 24rpx;
|
||||
margin-bottom: 28rpx;
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #8E8E93;
|
||||
}
|
||||
}
|
||||
/* 加载/失败/空配置居中态 */
|
||||
.wf-center {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.wf-center-text {
|
||||
font-size: 26rpx;
|
||||
color: #8E8E93;
|
||||
}
|
||||
.wf-retry {
|
||||
margin-top: 24rpx;
|
||||
padding: 14rpx 48rpx;
|
||||
background: #6ACDBB;
|
||||
border-radius: 40rpx;
|
||||
transition: opacity 0.2s ease;
|
||||
text {
|
||||
font-size: 26rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
.wf-retry-hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
176
components/workbench-user-entries/workbench-user-entries.vue
Normal file
176
components/workbench-user-entries/workbench-user-entries.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<!--
|
||||
工作台首页「个人功能区」共享组件(诊所管理员/平台管理员/业务员/诊所推广员四端复用)
|
||||
结构固定三段:最近使用(有记录才显示)→ 我的常用(空态给引导)→ 更多功能整行卡(进功能页)
|
||||
形态跟随卡片偏好:cardLong=true 长卡一行两个 / false 白卡容器内 4 列金刚区
|
||||
放主包 components/ 原因:四端首页分属不同分包,微信不允许跨分包引用组件(同 xk-service-card)
|
||||
-->
|
||||
<view class="wue-wrap">
|
||||
<!-- 最近使用:无记录整段隐藏(历史随点击自然累积,无需空态占位) -->
|
||||
<view v-if="recents.length" class="wue-section">
|
||||
<view class="wue-header">
|
||||
<text class="wue-title">最近使用</text>
|
||||
</view>
|
||||
<view v-if="cardLong" class="wue-grid is-long">
|
||||
<view v-for="item in recents" :key="item.key" class="wue-cell">
|
||||
<xk-service-card
|
||||
:icon="item.icon" :label="item.label" :desc="item.desc"
|
||||
:theme="item.theme" :badge="item.badge || 0"
|
||||
@click="onItemTap(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="wue-panel">
|
||||
<view class="wue-grid">
|
||||
<view v-for="item in recents" :key="item.key" class="wue-cell">
|
||||
<xk-service-card
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label"
|
||||
:theme="item.theme" :badge="item.badge || 0"
|
||||
@click="onItemTap(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 我的常用:空态给引导条(点击也进功能页),避免新用户首页空洞无从下手 -->
|
||||
<view class="wue-section">
|
||||
<view class="wue-header">
|
||||
<text class="wue-title">我的常用</text>
|
||||
</view>
|
||||
<view v-if="favorites.length && cardLong" class="wue-grid is-long">
|
||||
<view v-for="item in favorites" :key="item.key" class="wue-cell">
|
||||
<xk-service-card
|
||||
:icon="item.icon" :label="item.label" :desc="item.desc"
|
||||
:theme="item.theme" :badge="item.badge || 0"
|
||||
@click="onItemTap(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="favorites.length" class="wue-panel">
|
||||
<view class="wue-grid">
|
||||
<view v-for="item in favorites" :key="item.key" class="wue-cell">
|
||||
<xk-service-card
|
||||
mode="grid"
|
||||
:icon="item.icon" :label="item.label"
|
||||
:theme="item.theme" :badge="item.badge || 0"
|
||||
@click="onItemTap(item)"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="wue-hint" hover-class="wue-hint-hover" @tap="$emit('more')">
|
||||
<u-icon name="star" size="30" color="#8E8E93"></u-icon>
|
||||
<text class="wue-hint-text">暂无常用功能,去功能页长按任意入口即可添加</text>
|
||||
<u-icon name="arrow-right" size="22" color="#C7C7CC"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 更多功能:整行 iOS 行卡固定入口,通往功能页(全部入口在功能 tab) -->
|
||||
<view class="wue-section">
|
||||
<xk-service-card
|
||||
icon="grid-fill" label="更多功能" desc="查看全部功能入口"
|
||||
:theme="moreTheme"
|
||||
@click="$emit('more')"
|
||||
></xk-service-card>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* workbench-user-entries 首页个人功能区组件
|
||||
*
|
||||
* 为什么做成组件:四端首页从「铺全部功能入口」改为「只展示最近使用 + 我的常用 + 更多功能」,
|
||||
* 三段结构与双形态布局完全一致,收敛到主包组件避免四份复制。
|
||||
* 数据与行为职责划分:
|
||||
* - 父页面负责拉取 entry-user-data、按 code 注入本端行为(角标/表单预填/传方弹窗)后传入
|
||||
* - 本组件只管渲染与转发事件:@item-click(item) 点了某入口、@more 要去功能页
|
||||
* 点击上报使用也由父页面在 @item-click 里做(各端上报接口不同)
|
||||
*/
|
||||
export default {
|
||||
name: 'WorkbenchUserEntries',
|
||||
props: {
|
||||
/** 最近使用入口(已归一化 + 行为装饰) */
|
||||
recents: { type: Array, default: () => [] },
|
||||
/** 我的常用入口(已归一化 + 行为装饰) */
|
||||
favorites: { type: Array, default: () => [] },
|
||||
/** 卡片形态:true 长卡一行两个 / false 金刚区宫格 */
|
||||
cardLong: { type: Boolean, default: false },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
/** 「更多功能」卡磁贴主题:品牌青绿,与工作台主色一致 */
|
||||
moreTheme: { bg: 'linear-gradient(135deg, #ECFDF5, #D1FAE5)', color: '#6ACDBB' },
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** 入口点击统一转发:跳转/上报/特殊行为都由父页面处理 */
|
||||
onItemTap(item) {
|
||||
this.$emit('item-click', item);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wue-section {
|
||||
margin: 0 30rpx 30rpx;
|
||||
}
|
||||
.wue-header {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
/* 分区标题:对齐四首页既有 panel-title 规格 */
|
||||
.wue-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B;
|
||||
}
|
||||
/* 金刚区白卡容器:iOS 卡片规格(大圆角 + 发丝线 + 轻阴影) */
|
||||
.wue-panel {
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 16rpx 12rpx;
|
||||
}
|
||||
/* 金刚区宫格:4 列图标磁贴(cardLong=false 缺省形态);minmax(0,1fr) 防长文案撑破轨道 */
|
||||
.wue-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
row-gap: 8rpx;
|
||||
}
|
||||
/* 长卡片模式:一行两张 iOS 行卡(卡内标题/描述单行省略,窄卡不溢出) */
|
||||
.wue-grid.is-long {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16rpx;
|
||||
}
|
||||
/* 卡片外层 cell:min-width:0 让内部文本省略生效 */
|
||||
.wue-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
/* 常用空态引导条:白卡内浅灰引导,点击直达功能页 */
|
||||
.wue-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 32rpx 28rpx;
|
||||
}
|
||||
.wue-hint-hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.wue-hint-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin: 0 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #8E8E93;
|
||||
}
|
||||
</style>
|
||||
301
components/xk-list-card/xk-list-card.vue
Normal file
301
components/xk-list-card/xk-list-card.vue
Normal file
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<!--
|
||||
通用业务列表卡片(对齐医生工作台 flat-card 设计规范)
|
||||
结构:头像区(可选) + 标题行(标题/meta/标签/右侧时间) + 内容行(插槽或 rows) + 底部行(状态 + 主/次操作按钮)
|
||||
用法示例:
|
||||
<xk-list-card
|
||||
:title="item.name" :meta="'男 · 32岁'" :time="item.created_at"
|
||||
:tags="[{ text: '线上', type: 'primary' }]"
|
||||
:rows="['手机号:138xxxx']"
|
||||
:status="{ text: '待审核', color: '#FF9900' }"
|
||||
:actions="[{ key: 'edit', text: '编辑', type: 'ghost' }, { key: 'detail', text: '查看', type: 'primary' }]"
|
||||
@action="onAction" @click="onCardTap"
|
||||
/>
|
||||
-->
|
||||
<view :class="cardClass" hover-class="xk-card-hover" @tap="onCardTap">
|
||||
<!-- 头像区:传 avatar 图片,或 avatar-text 首字圆底,二选一;支持未读角标 -->
|
||||
<view class="xk-avatar-wrap" v-if="showAvatarArea">
|
||||
<slot name="avatar">
|
||||
<u-image v-if="avatar" width="80rpx" height="80rpx" shape="circle" :src="avatar">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<view v-else class="xk-avatar-text">{{ avatarChar }}</view>
|
||||
</slot>
|
||||
<u-badge v-if="badgeCount > 0" :count="badgeCount" :offset="[-4, -4]" bgColor="#F44336" :overflow-count="99"></u-badge>
|
||||
</view>
|
||||
|
||||
<view class="xk-card-body flex-col flex-1">
|
||||
<!-- 第一行:标题 + meta + 标签,右侧时间 -->
|
||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
||||
<view class="flex-row flex-ali-center flex-1 text-hide">
|
||||
<text class="xk-name text-hide">{{ title }}</text>
|
||||
<text class="xk-meta" v-if="meta">{{ meta }}</text>
|
||||
<!-- :key 直接用下标:非 h5 平台不支持 'tag-'+ti 这类拼接表达式 -->
|
||||
<u-tag v-for="(tag, ti) in tags" :key="ti" class="xk-tag"
|
||||
:text="tag.text" :type="tag.type || 'info'" size="mini"></u-tag>
|
||||
<slot name="title-extra"></slot>
|
||||
</view>
|
||||
<text class="xk-time" v-if="time">{{ time }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区:优先用 body 插槽,否则渲染 rows 文本行 -->
|
||||
<slot name="body">
|
||||
<text v-for="(row, ri) in rows" :key="ri" class="xk-sub text-hide m-t-8">{{ row }}</text>
|
||||
</slot>
|
||||
|
||||
<!-- 底部行:左状态 + 右操作按钮(有其一才渲染,避免空白高度) -->
|
||||
<view class="flex-row flex-jus-sp flex-ali-center m-t-12" v-if="showFooter">
|
||||
<view class="xk-status flex-row flex-ali-center text-hide">
|
||||
<slot name="footer-left">
|
||||
<text v-if="status && status.text" class="xk-status-text" :style="{ color: status.color || '#999' }">{{ status.text }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="xk-actions flex-row flex-ali-center">
|
||||
<slot name="footer-right">
|
||||
<view v-for="(act, ai) in actions" :key="ai"
|
||||
:class="['xk-action-btn', act.type === 'primary' ? 'btn-primary' : (act.type === 'danger' ? 'btn-danger' : 'btn-ghost')]"
|
||||
@tap.stop="onActionTap(act)">{{ act.text }}</view>
|
||||
</slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧箭头:无操作按钮的纯跳转卡可开启,暗示整卡可点 -->
|
||||
<view class="xk-arrow" v-if="arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* xk-list-card 通用业务列表卡片
|
||||
* 为什么要做这个组件:医生端各角色的列表卡(患者/门店/推广员/获客/传方等)此前是十几处
|
||||
* 各写各的白底矩形,信息层级和按钮风格不统一。统一抽成本组件后,所有列表卡自动对齐
|
||||
* 医生工作台 flat-card 的设计规范(信息分层 + 状态色 + 主/次操作按钮 + 青绿主色)。
|
||||
*/
|
||||
export default {
|
||||
name: 'xk-list-card',
|
||||
props: {
|
||||
/** 卡片形态:card=独立白卡(灰底页面用,默认);flat=平铺分割线(白底容器内用) */
|
||||
mode: { type: String, default: 'card' },
|
||||
/** 头像图片地址;为空且 avatarText 也为空时不渲染头像区 */
|
||||
avatar: { type: String, default: '' },
|
||||
/** 头像兜底文字(一般取名字首字),渲染为青绿圆底白字 */
|
||||
avatarText: { type: String, default: '' },
|
||||
/** 头像上的未读/待办角标数 */
|
||||
badgeCount: { type: Number, default: 0 },
|
||||
/** 标题(姓名/门店名等),必填 */
|
||||
title: { type: String, default: '' },
|
||||
/** 标题右侧灰字 meta(性别年龄/编号等) */
|
||||
meta: { type: String, default: '' },
|
||||
/** 第一行右侧时间文案 */
|
||||
time: { type: String, default: '' },
|
||||
/** 标签数组 [{ text, type }],type 对应 u-tag 的 type(primary/success/warning/error/info) */
|
||||
tags: { type: Array, default: () => [] },
|
||||
/** 内容文本行数组,每项一行灰字;复杂内容用 body 插槽 */
|
||||
rows: { type: Array, default: () => [] },
|
||||
/** 底部左侧状态 { text, color } */
|
||||
status: { type: Object, default: null },
|
||||
/** 底部右侧操作按钮 [{ key, text, type: 'primary'|'ghost'|'danger' }],点击 emit('action', act) */
|
||||
actions: { type: Array, default: () => [] },
|
||||
/** 是否显示右侧箭头(纯跳转卡用,与 actions 二选一) */
|
||||
arrow: { type: Boolean, default: false },
|
||||
/** 整卡是否可点击(控制 hover 反馈;点击 emit('click')) */
|
||||
clickable: { type: Boolean, default: true }
|
||||
},
|
||||
computed: {
|
||||
/** 是否渲染头像区:有图或有兜底文字才占位,避免无头像场景左侧空白 */
|
||||
showAvatarArea() {
|
||||
return !!(this.avatar || this.avatarText || this.$slots.avatar)
|
||||
},
|
||||
/** 头像兜底字符:取 avatarText 首字 */
|
||||
avatarChar() {
|
||||
return this.avatarText ? this.avatarText.slice(0, 1) : ''
|
||||
},
|
||||
/** 底部行是否渲染:有状态、操作或对应插槽才渲染 */
|
||||
showFooter() {
|
||||
const hasStatus = !!(this.status && this.status.text)
|
||||
return hasStatus || this.actions.length > 0 || !!this.$slots['footer-left'] || !!this.$slots['footer-right']
|
||||
},
|
||||
/** 卡片根 class:小程序模板禁止 :class 里调函数,这里用 computed 计算 */
|
||||
cardClass() {
|
||||
return ['xk-list-card', 'flex-row', 'flex-ali-start', this.mode === 'flat' ? 'is-flat' : 'is-card', this.clickable ? 'is-clickable' : '']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 整卡点击:透传给父级(一般跳详情) */
|
||||
onCardTap() {
|
||||
if (this.clickable) this.$emit('click')
|
||||
},
|
||||
/** 操作按钮点击:stop 阻止冒泡避免同时触发整卡点击 */
|
||||
onActionTap(act) {
|
||||
this.$emit('action', act)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* ===== 局部工具类 =====
|
||||
微信自定义组件默认隔离 app.wxss(styleIsolation),全局原子类进不来,
|
||||
所以组件内自带一份用到的布局工具类,保证任何页面引用都不会跑版 */
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
.flex-ali-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.flex-ali-center {
|
||||
align-items: center;
|
||||
}
|
||||
.flex-jus-sp {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.text-hide {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.m-t-8 {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.m-t-12 {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
/* ===== 卡片容器:两种形态 ===== */
|
||||
.xk-list-card {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 独立白卡:灰底页面上的悬浮卡,与工作台看板同规格圆角阴影
|
||||
横向留白由页面容器控制(各页 padding 24rpx),组件只负责纵向间距 */
|
||||
.is-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
/* 平铺形态:白底容器内的分割线列表(对齐工作台 flat-card) */
|
||||
.is-flat {
|
||||
padding: 24rpx 20rpx;
|
||||
border-bottom: 1rpx solid #f5f6f8;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
/* 点击按压反馈:轻微下沉 + 底色变化 */
|
||||
.xk-card-hover {
|
||||
&.is-card {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
&.is-flat {
|
||||
background: #fafbfc;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 头像区 ===== */
|
||||
.xk-avatar-wrap {
|
||||
width: 80rpx;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
/* 无头像图时的首字兜底:青绿浅底 + 主色文字 */
|
||||
.xk-avatar-text {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background: #e8f7f3;
|
||||
color: #4db6a8;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ===== 信息分层(与工作台 .wb-* 同规格) ===== */
|
||||
.xk-card-body {
|
||||
width: auto !important;
|
||||
min-width: 0;
|
||||
}
|
||||
.xk-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.xk-meta {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
}
|
||||
.xk-tag {
|
||||
margin-left: 12rpx;
|
||||
flex-shrink: 0;
|
||||
transform: scale(0.9);
|
||||
transform-origin: left center;
|
||||
}
|
||||
.xk-time {
|
||||
flex-shrink: 0;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
.xk-sub {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.xk-status-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
/* ===== 操作按钮:主实心青绿 / 次浅底描边 / 危险红 ===== */
|
||||
.xk-actions {
|
||||
flex-shrink: 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
.xk-action-btn {
|
||||
padding: 10rpx 28rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 30rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background: #6ACDBB;
|
||||
}
|
||||
.btn-ghost {
|
||||
color: #6ACDBB;
|
||||
background: #f0f9f7;
|
||||
border: 1rpx solid #c2ece2;
|
||||
}
|
||||
.btn-danger {
|
||||
color: #fa3534;
|
||||
background: #fef0f0;
|
||||
border: 1rpx solid #fbc4c4;
|
||||
}
|
||||
|
||||
/* 右侧箭头 */
|
||||
.xk-arrow {
|
||||
flex-shrink: 0;
|
||||
margin-left: 12rpx;
|
||||
align-self: center;
|
||||
}
|
||||
</style>
|
||||
200
components/xk-service-card/xk-service-card.vue
Normal file
200
components/xk-service-card/xk-service-card.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<!--
|
||||
工作台功能入口卡(iOS 风格,诊所管理员/平台管理员/业务员/诊所推广员四端复用)
|
||||
两种形态由 mode 控制:
|
||||
- list(缺省):iOS 行卡(磁贴 + 标题/描述 + chevron),长卡片模式与首页整行列表用
|
||||
- grid:金刚区竖排项(磁贴 + 标签,透明底),首页宫格模式与功能页用,外层白卡容器由页面提供
|
||||
用法:
|
||||
<xk-service-card :icon="item.icon" :label="item.label" :desc="item.desc"
|
||||
:theme="item.theme" :badge="item.badge" mode="grid" @click="go(item)" @longpress="onLongpress(item)" />
|
||||
-->
|
||||
<view
|
||||
v-if="mode !== 'grid'"
|
||||
class="xk-service-card" hover-class="xk-service-card-hover" :hover-stay-time="60"
|
||||
@tap="$emit('click')" @longpress="$emit('longpress')"
|
||||
>
|
||||
<!-- 磁贴底色用内联对象字面量绑定:computed 返回对象在 mp-weixin 端曾出现 style 未生效,字面量是旧版验证过的稳妥写法 -->
|
||||
<view class="icon-tile" :style="{ background: (theme && theme.color) || '#8E8E93' }">
|
||||
<u-icon :name="icon" size="40" color="#FFFFFF"></u-icon>
|
||||
<!-- iOS 风格顶部高光层:叠白色渐变模拟系统图标的微立体质感 -->
|
||||
<view class="icon-tile-gloss"></view>
|
||||
<!-- 待办角标:iOS 系统红(#FF3B30),挂在磁贴右上角 -->
|
||||
<u-badge v-if="badge > 0" :count="badge" :offset="[-10, -10]" bgColor="#FF3B30" :overflow-count="99"></u-badge>
|
||||
</view>
|
||||
<view class="info">
|
||||
<text class="name">{{ label }}</text>
|
||||
<text class="desc">{{ desc || '快捷进入' }}</text>
|
||||
</view>
|
||||
<!-- iOS disclosure chevron:弱化的可点击暗示 -->
|
||||
<view class="arrow">
|
||||
<u-icon name="arrow-right" size="22" color="#C7C7CC"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- grid 金刚区形态:竖排图标 + 标签,无描述无 chevron,信息密度高 -->
|
||||
<view
|
||||
v-else
|
||||
class="xk-service-cell" hover-class="xk-service-cell-hover" :hover-stay-time="60"
|
||||
@tap="$emit('click')" @longpress="$emit('longpress')"
|
||||
>
|
||||
<view class="icon-tile is-grid" :style="{ background: (theme && theme.color) || '#8E8E93' }">
|
||||
<u-icon :name="icon" size="44" color="#FFFFFF"></u-icon>
|
||||
<view class="icon-tile-gloss"></view>
|
||||
<u-badge v-if="badge > 0" :count="badge" :offset="[-8, -8]" bgColor="#FF3B30" :overflow-count="99"></u-badge>
|
||||
</view>
|
||||
<text class="cell-label">{{ label }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* xk-service-card 工作台功能入口卡(iOS 风格版)
|
||||
*
|
||||
* 为什么这样封装:
|
||||
* 1. 四个管理员端首页分属四个不同分包,微信不允许跨分包引用组件,
|
||||
* 共享组件源码必须放主包 components/(easycom 自动注册);
|
||||
* manifest 已开启 optimization.subPackages + lazyCodeLoading,
|
||||
* 组件代码按需注入,不会造成主包冗余加载
|
||||
* 2. props 与旧版兼容(icon/label/desc/theme/badge),新增 mode 切换行卡/金刚区两种形态,
|
||||
* 后台 xk_wx_workbench_entry 下发的 theme JSON({bg,color})无需变更
|
||||
*
|
||||
* iOS 风格要点:
|
||||
* - 图标磁贴取 theme.color 作纯色底 + 白色图标(对齐 iOS 设置页图标规范),
|
||||
* 旧版的浅色渐变底 theme.bg 保留兼容但不再使用
|
||||
* - 行卡:大圆角 + 发丝线描边 + 极轻阴影;按压回弹缩放(iOS spring 手感)
|
||||
* - 金刚区:透明底竖排,按压整体缩放;外层白色容器卡由页面统一提供
|
||||
*
|
||||
* 事件:@click 点击进入;@longpress 长按(功能页用于收藏,不绑定则无行为)
|
||||
*/
|
||||
export default {
|
||||
name: 'xk-service-card',
|
||||
props: {
|
||||
/** uView 图标名 */
|
||||
icon: { type: String, default: 'grid-fill' },
|
||||
/** 入口标题 */
|
||||
label: { type: String, default: '' },
|
||||
/** 入口描述(空则显示「快捷进入」,grid 形态不展示) */
|
||||
desc: { type: String, default: '' },
|
||||
/** 图标区主题 { bg: 渐变底(兼容保留), color: 磁贴底色 } */
|
||||
theme: {
|
||||
type: Object,
|
||||
default: () => ({ bg: 'linear-gradient(135deg, #F3F4F6, #E5E7EB)', color: '#8E8E93' })
|
||||
},
|
||||
/** 待办角标数,>0 时显示 */
|
||||
badge: { type: Number, default: 0 },
|
||||
/** 形态:list 行卡(缺省)/ grid 金刚区竖排项 */
|
||||
mode: { type: String, default: 'list' }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* iOS 卡片容器:大圆角 + 发丝线 + 极轻弥散阴影,按压回弹缩放 */
|
||||
.xk-service-card {
|
||||
position: relative;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
transition: transform 0.25s cubic-bezier(0.34, 1.3, 0.64, 1), box-shadow 0.25s ease, background 0.25s ease;
|
||||
}
|
||||
.xk-service-card-hover {
|
||||
transform: scale(0.965);
|
||||
background: #F7F7FA;
|
||||
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
/* iOS 图标磁贴:主题纯色底 + 白色图标,圆角率对齐系统图标(约 25%),兼作角标定位容器 */
|
||||
/* CSS 里给兜底灰底:即使内联主题色因异常数据缺失,白色图标也不会「隐身」在白卡上 */
|
||||
.icon-tile {
|
||||
position: relative;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #8E8E93;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 22rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* 磁贴高光层:上白下微暗的渐变叠加,模拟 iOS 图标的立体光泽(不挡角标,角标在其后渲染层级更高) */
|
||||
.icon-tile-gloss {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0.05) 45%, rgba(0, 0, 0, 0.05) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
/* 标题/描述:对齐 iOS label / secondaryLabel 配色,均单行省略防止长文案撑破卡片 */
|
||||
.name {
|
||||
font-size: 29rpx;
|
||||
font-weight: 600;
|
||||
color: #1C1C1E;
|
||||
line-height: 1.25;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.desc {
|
||||
font-size: 22rpx;
|
||||
color: #8E8E93;
|
||||
margin-top: 6rpx;
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* 右侧 chevron:iOS disclosure 灰 */
|
||||
.arrow {
|
||||
flex-shrink: 0;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
/* ---- grid 金刚区形态 ---- */
|
||||
/* 竖排项:透明底(外层白卡由页面提供),按压整体缩放 + 微降透明度 */
|
||||
.xk-service-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 4rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: transform 0.2s cubic-bezier(0.34, 1.3, 0.64, 1), opacity 0.2s ease;
|
||||
}
|
||||
.xk-service-cell-hover {
|
||||
transform: scale(0.92);
|
||||
opacity: 0.75;
|
||||
}
|
||||
/* 金刚区磁贴:比行卡略大(88rpx),圆角率同步放大,去掉行卡的右边距 */
|
||||
.icon-tile.is-grid {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-right: 0;
|
||||
}
|
||||
.icon-tile.is-grid .icon-tile-gloss {
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
/* 金刚区标签:单行省略,宽度受 cell 约束 */
|
||||
.cell-label {
|
||||
margin-top: 12rpx;
|
||||
max-width: 100%;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #1C1C1E;
|
||||
line-height: 1.2;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
147
pages.json
147
pages.json
@@ -12,13 +12,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/register",
|
||||
"style": {
|
||||
"navigationBarTitleText": "注册",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/workbench/index",
|
||||
"style": {
|
||||
@@ -86,31 +79,12 @@
|
||||
"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": {
|
||||
@@ -119,44 +93,12 @@
|
||||
"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/addProductServicePackage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加产品服务包"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "workbench_recipe/common",
|
||||
"style": {
|
||||
@@ -182,24 +124,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "workbench_basicInfo",
|
||||
"style": {
|
||||
"navigationBarTitleText": "基础健康信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "workbench_inquiries",
|
||||
"style": {
|
||||
"navigationBarTitleText": "问诊记录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "workbench_used_language",
|
||||
"style": {
|
||||
"navigationBarTitleText": "常用语管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "workbench_code",
|
||||
"style": {
|
||||
@@ -249,18 +173,6 @@
|
||||
"navigationBarTitleText": "群发信息"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "article_select",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择文章"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "patient_details",
|
||||
"style": {
|
||||
"navigationBarTitleText": "患者详情"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "to-store",
|
||||
"style": {
|
||||
@@ -342,13 +254,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "im",
|
||||
"style": {
|
||||
"navigationBarTitleText": "咨询"
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pharmacist_setInfo",
|
||||
"style": {
|
||||
@@ -356,20 +261,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "change_password",
|
||||
"style": {
|
||||
"navigationBarTitleText": "修改登录密码"
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pharmacist_prescription",
|
||||
"style": {
|
||||
"navigationBarTitleText": "查看处方",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pharmacist_traceability",
|
||||
"style": {
|
||||
@@ -377,20 +268,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pharmacist_access",
|
||||
"style": {
|
||||
"navigationBarTitleText": "待接入用户"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pharmacist_triage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择医生",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
"path": "myrecord-detail",
|
||||
"style": {
|
||||
@@ -406,6 +283,10 @@
|
||||
"path": "home/index",
|
||||
"style": { "navigationBarTitleText": "工作台", "navigationStyle": "custom" }
|
||||
},
|
||||
{
|
||||
"path": "features/index",
|
||||
"style": { "navigationBarTitleText": "功能", "navigationStyle": "custom" }
|
||||
},
|
||||
{
|
||||
"path": "my/index",
|
||||
"style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" }
|
||||
@@ -459,6 +340,7 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情",
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": true,
|
||||
"usingComponents": {
|
||||
"order-price-percent-adjust-popup": "/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup"
|
||||
},
|
||||
@@ -483,8 +365,9 @@
|
||||
}, {
|
||||
"root": "subPackages/sub_business_shared",
|
||||
"pages": [
|
||||
{ "path": "card-style/index", "style": { "navigationBarTitleText": "卡片样式", "navigationStyle": "custom" } },
|
||||
{ "path": "order/index", "style": { "navigationBarTitleText": "商品订单", "navigationStyle": "custom", "disableScroll": true } },
|
||||
{ "path": "order/detail", "style": { "navigationBarTitleText": "订单详情", "navigationStyle": "custom" } },
|
||||
{ "path": "order/detail", "style": { "navigationBarTitleText": "订单详情", "navigationStyle": "custom", "enablePullDownRefresh": true } },
|
||||
{ "path": "order/trace/index", "style": { "navigationBarTitleText": "订单追溯", "navigationStyle": "custom" } },
|
||||
{ "path": "reconciliation/index", "style": { "navigationBarTitleText": "对账单", "navigationStyle": "custom" } },
|
||||
{ "path": "withdrawal/index", "style": { "navigationBarTitleText": "提现管理", "navigationStyle": "custom" } },
|
||||
@@ -499,12 +382,14 @@
|
||||
{ "path": "user-patient/index", "style": { "navigationBarTitleText": "会员管理", "navigationStyle": "custom" } },
|
||||
{ "path": "user-patient/patients", "style": { "navigationBarTitleText": "就诊人列表", "navigationStyle": "custom" } },
|
||||
{ "path": "user-patient/detail", "style": { "navigationBarTitleText": "就诊人详情", "navigationStyle": "custom" } },
|
||||
{ "path": "user-patient/register-detail", "style": { "navigationBarTitleText": "挂号详情", "navigationStyle": "custom" } }
|
||||
{ "path": "user-patient/register-detail", "style": { "navigationBarTitleText": "挂号详情", "navigationStyle": "custom" } },
|
||||
{ "path": "profile/index", "style": { "navigationBarTitleText": "个人中心", "navigationStyle": "custom" } }
|
||||
]
|
||||
}, {
|
||||
"root": "subPackages/sub_platform_admin",
|
||||
"pages": [
|
||||
{ "path": "home/index", "style": { "navigationBarTitleText": "工作台", "navigationStyle": "custom" } },
|
||||
{ "path": "features/index", "style": { "navigationBarTitleText": "功能", "navigationStyle": "custom" } },
|
||||
{ "path": "my/index", "style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" } },
|
||||
{ "path": "withdrawal/audit/index", "style": { "navigationBarTitleText": "提现审核", "navigationStyle": "custom" } },
|
||||
{ "path": "store/index", "style": { "navigationBarTitleText": "门店管理", "navigationStyle": "custom" } },
|
||||
@@ -533,6 +418,7 @@
|
||||
"root": "subPackages/sub_salesperson",
|
||||
"pages": [
|
||||
{ "path": "home/index", "style": { "navigationBarTitleText": "工作台", "navigationStyle": "custom" } },
|
||||
{ "path": "features/index", "style": { "navigationBarTitleText": "功能", "navigationStyle": "custom" } },
|
||||
{ "path": "my/index", "style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" } },
|
||||
{ "path": "store-input/index", "style": { "navigationBarTitleText": "列表", "navigationStyle": "custom" } },
|
||||
{ "path": "store-input/form", "style": { "navigationBarTitleText": "录入", "navigationStyle": "custom" } },
|
||||
@@ -558,14 +444,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "features/index",
|
||||
"style": { "navigationBarTitleText": "功能", "navigationStyle": "custom" }
|
||||
},
|
||||
{ "path": "my/index", "style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" } },
|
||||
{ "path": "earnings/index", "style": { "navigationBarTitleText": "我的收益", "navigationStyle": "custom" } },
|
||||
{ "path": "leads/index", "style": { "navigationBarTitleText": "获客记录", "navigationStyle": "custom" } },
|
||||
{ "path": "commission/index", "style": { "navigationBarTitleText": "服务费记录", "navigationStyle": "custom" } },
|
||||
{ "path": "invitees/index", "style": { "navigationBarTitleText": "我邀请的推广员", "navigationStyle": "custom", "enablePullDownRefresh": true } },
|
||||
{ "path": "invitees/detail", "style": { "navigationBarTitleText": "收益明细", "navigationStyle": "custom" } },
|
||||
{ "path": "settlement/index", "style": { "navigationBarTitleText": "结算记录", "navigationStyle": "custom" } },
|
||||
{ "path": "transfer-prescription/form", "style": { "navigationBarTitleText": "传方", "navigationStyle": "custom" } },
|
||||
{ "path": "transfer-prescription/list", "style": { "navigationBarTitleText": "传方记录", "navigationStyle": "custom" } }
|
||||
]
|
||||
}, {
|
||||
@@ -575,11 +463,6 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "协议"
|
||||
}
|
||||
}, {
|
||||
"path": "web-view",
|
||||
"style": {
|
||||
"navigationBarTitleText": "视频"
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
"root": "subPackages/sub_online_reception",
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
<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';
|
||||
import { isPhone, assertPhone } from '@/utils/phone.js';
|
||||
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() {
|
||||
// 与后端 checkPhone 统一号段,不用 uView 默认 mobile 规则
|
||||
if (!isPhone(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'])
|
||||
|
||||
// 存储doctor_id或user_id,用于WebSocket绑定
|
||||
if (res.data['user'] && res.data['user']['id']) {
|
||||
uni.setStorageSync('doctor_id', res.data['user']['id']);
|
||||
uni.setStorageSync('user_id', res.data['user']['id']);
|
||||
}
|
||||
|
||||
// 触发WebSocket初始化(注册成功后)
|
||||
uni.$emit('user-login');
|
||||
|
||||
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) {
|
||||
if (!assertPhone(this.form['mobile'], '请输入手机号', '请输入正确的手机号')) {
|
||||
return
|
||||
}
|
||||
// 模拟向后端请求验证码
|
||||
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>
|
||||
@@ -104,10 +104,6 @@
|
||||
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: '/subPackages/sub_my/prescriptionList'
|
||||
@@ -129,20 +125,6 @@
|
||||
}
|
||||
],
|
||||
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',
|
||||
|
||||
@@ -16,13 +16,6 @@
|
||||
<view class="wx-bind-wrap p-col-24">
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
</view>
|
||||
<view class="p-col-32 bottom-border" @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 class="p-col-32 bottom-border flex-jus-sp flex-row flex-ali-center" @click="$go()">
|
||||
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="kefu-ermai" label="联系客服" size="40"
|
||||
color="#6C7380"></u-icon>
|
||||
|
||||
@@ -8,64 +8,53 @@
|
||||
<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"
|
||||
<view class="entry-card flex-1" hover-class="entry-card-hover"
|
||||
@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>
|
||||
<image class="entry-icon" src="../../static/image/hz_xsjz.png"></image>
|
||||
<view class="entry-info">
|
||||
<text class="entry-name">到店接诊</text>
|
||||
<text class="entry-desc">处理到店患者</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="function white b-r-8 flex-row p-32 flex-1 m-l-2"
|
||||
<view class="entry-card flex-1 m-l-2" hover-class="entry-card-hover"
|
||||
@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>
|
||||
<image class="entry-icon" src="../../static/image/hz_ddjz.png"></image>
|
||||
<view class="entry-info">
|
||||
<text class="entry-name">线上接诊</text>
|
||||
<text class="entry-desc">线上问诊接待</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-new" @click="$go('../../subPackages/sub_patient/patient_massInfo')">
|
||||
<!-- 群发消息入口行 -->
|
||||
<view class="list-new" hover-class="entry-card-hover" @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.patient_id)">
|
||||
<u-image width="80rpx" height="80rpx" shape="circle"
|
||||
:src="item.user.avatarurl || 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 class="entry-info">
|
||||
<text class="entry-name">群发消息</text>
|
||||
<text class="entry-desc">向患者批量发送通知</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#C0C4CC"></u-icon>
|
||||
</view>
|
||||
<!-- 患者列表:白底容器 + flat 形态列表卡(信息分层 + 箭头暗示可点) -->
|
||||
<view class="flex-col b-r-8 white patient-panel m-t-2">
|
||||
<view class="title flex-row flex-jus-sp patient-panel-title">
|
||||
<d-text text="患者管理" className="main-c m-r-16 fs-32"></d-text>
|
||||
</view>
|
||||
<xk-list-card
|
||||
v-for="(item, index) in list" :key="index"
|
||||
mode="flat"
|
||||
:avatar="item.user.avatarurl || require(`../../static/image/${item.sex%2==0?'nv':'nan'}.png`)"
|
||||
:title="item.patient"
|
||||
:meta="(item.sex % 2 != 0 ? '男' : '女') + ' · ' + item.age + '岁'"
|
||||
:arrow="true"
|
||||
@click="toDetail(item.patient_id)"
|
||||
></xk-list-card>
|
||||
<view class="patient-empty" v-if="!loading && list.length == 0">
|
||||
<text>暂无患者,接诊后自动建立档案</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
@@ -198,29 +187,60 @@
|
||||
z-index: 982;
|
||||
}
|
||||
|
||||
.function {
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
/* 接诊入口双列卡:白卡大圆角 + 轻阴影,与工作台宫格同规格 */
|
||||
.entry-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.l-19 {
|
||||
.entry-card-hover {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.entry-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 入口卡文案分层:标题 + 灰字描述 */
|
||||
.entry-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-left: 20rpx;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.entry-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #31353D;
|
||||
}
|
||||
|
||||
.entry-desc {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.list-new {
|
||||
width: 710rpx;
|
||||
height: 108rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 20rpx 0;
|
||||
padding: 0 32rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
color: #333333;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
|
||||
.l-l-row {
|
||||
display: flex;
|
||||
@@ -229,9 +249,30 @@
|
||||
image {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
margin-right: 12rpx;
|
||||
vertical-align: middle;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 患者管理白底容器:标题带内边距,列表项走 flat 卡自带分割线 */
|
||||
.patient-panel {
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 12rpx 16rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.patient-panel-title {
|
||||
padding: 0 20rpx 8rpx;
|
||||
}
|
||||
|
||||
/* 空态提示 */
|
||||
.patient-empty {
|
||||
padding: 60rpx 0;
|
||||
text-align: center;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,95 +1,56 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom">
|
||||
<view class="page-wrap 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 -->
|
||||
<!-- 头部 tabs:与下方 swiper 双向联动(点 tab / 左右滑都走 switchTab) -->
|
||||
<view class="head">
|
||||
<u-tabs :list="lists" :is-scroll="false" :current="currents" @change="change" active-color="#6ACDBB"
|
||||
<u-tabs :list="lists" :is-scroll="false" :current="currents" @change="switchTab" active-color="#6ACDBB"
|
||||
:height="99">
|
||||
</u-tabs>
|
||||
</view>
|
||||
|
||||
<!-- 空页 -->
|
||||
<view class="empty" v-if="listInfo.length==0 ||status!=2">
|
||||
<!-- 未通过入驻审核:整页空态守卫,不渲染 swiper -->
|
||||
<view class="empty" v-if="status != 2">
|
||||
<image src="/static/image/none.png" mode="aspectFit"></image>
|
||||
<text v-if="status!=2">您还没有通过审核,请稍后再试~</text>
|
||||
<text>您还没有通过审核,请稍后再试~</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> -->
|
||||
<!-- 审方列表:swiper 承载三个状态 tab,支持左右滑动切换;每个 tab 独立分页 + 上拉加载 -->
|
||||
<swiper v-else class="tab-swiper" :current="currents" @change="onSwiperChange">
|
||||
<swiper-item v-for="(tab, tabIndex) in tabPages" :key="tab.status">
|
||||
<scroll-view scroll-y class="tab-scroll" @scrolltolower="loadMore(tabIndex)">
|
||||
<view class="list-wrap">
|
||||
<xk-list-card
|
||||
v-for="item in tab.list" :key="item.id"
|
||||
:avatar-text="item.name"
|
||||
:title="item.name"
|
||||
:meta="item.meta"
|
||||
:time="item.time"
|
||||
:tags="[item.tag]"
|
||||
:rows="['处方号:' + item.prescription_no]"
|
||||
:status="{ text: lists[tabIndex].name, color: tabStatusColors[tabIndex] }"
|
||||
:actions="[{ key: 'detail', text: tabIndex === 0 ? '去审方' : '查看详情', type: tabIndex === 0 ? 'primary' : 'ghost' }]"
|
||||
@action="toDetail(item.id, item.prescription_no)"
|
||||
@click="toDetail(item.id, item.prescription_no)"
|
||||
></xk-list-card>
|
||||
|
||||
<!-- 中药 -->
|
||||
<!-- <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}} {{(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="success" />
|
||||
</view>
|
||||
{{item.status==0&&'待审核' || item.status==1&&'已通过' || item.status==2&&'未通过'}}
|
||||
</view>
|
||||
</view> -->
|
||||
<!-- 各 tab 独立空态:首屏加载完成且无数据时展示 -->
|
||||
<view class="tab-empty" v-if="tab.loaded && tab.list.length === 0">
|
||||
<image src="/static/image/none.png" mode="aspectFit"></image>
|
||||
<text>暂无{{lists[tabIndex].name}}的处方</text>
|
||||
</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 class="load-more" v-if="tab.list.length > 0">
|
||||
<text v-if="tab.loading">加载中...</text>
|
||||
<text v-else-if="tab.finished">没有更多了</text>
|
||||
</view>
|
||||
</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="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>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
|
||||
<u-tabbar v-model="current" :list="listed" active-color="#6ACDBB" @change="changed"></u-tabbar>
|
||||
@@ -97,25 +58,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 审方列表页:老 Yii examine/list 已迁移到 xk-api(/pharmacist-examine/list,标准分页)
|
||||
// 交互升级:u-tabs + swiper 双向联动(左右滑切 tab),每个 tab 独立数据缓存,切换不丢已加载内容
|
||||
import {
|
||||
getUserInfo,
|
||||
prescripRecord
|
||||
getUserInfo
|
||||
} from "@/api/all.js";
|
||||
import {
|
||||
getExamineList
|
||||
} from "@/api/pharmacistExamine.js";
|
||||
import {
|
||||
unwrapXkApi
|
||||
} from "@/utils/api-response.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: [{
|
||||
@@ -128,81 +88,144 @@
|
||||
name: '未通过',
|
||||
}
|
||||
],
|
||||
currents: 0, // 审核状态
|
||||
// 审核状态色:橙=待审核 / 绿=已通过 / 红=未通过(数组下标对应 tab)
|
||||
tabStatusColors: ['#FF7D00', '#00B42A', '#F53F3F'],
|
||||
currents: 0, // 当前选中 tab(tabs 与 swiper 共用)
|
||||
// 每个 tab 独立分页状态:切 tab 懒加载首屏,已加载的数据不重复请求
|
||||
tabPages: [
|
||||
{ status: 0, list: [], page: 1, finished: false, loading: false, loaded: false },
|
||||
{ status: 1, list: [], page: 1, finished: false, loading: false, loaded: false },
|
||||
{ status: 2, list: [], page: 1, finished: false, loading: false, loaded: false },
|
||||
],
|
||||
};
|
||||
},
|
||||
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()
|
||||
// 每次回到页面刷新当前 tab(审方通过/驳回返回后能看到最新状态),其它 tab 保留缓存
|
||||
this.refreshTab(this.currents)
|
||||
if (this.status != 2) {
|
||||
this.getKnow()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change(index) {
|
||||
this.currents = index;
|
||||
this.getList()
|
||||
/** tabs 与 swiper 统一切换入口:点 tab、手势左右滑都收敛到这里 */
|
||||
switchTab(index) {
|
||||
if (this.currents === index) return
|
||||
this.currents = index
|
||||
// 懒加载:该 tab 从未加载过才拉首屏,避免每次切换全量重刷
|
||||
if (!this.tabPages[index].loaded) {
|
||||
this.refreshTab(index)
|
||||
}
|
||||
},
|
||||
/** swiper 手势滑动回调:回写 currents 让顶部 tab 高亮跟随 */
|
||||
onSwiperChange(e) {
|
||||
this.switchTab(e.detail.current)
|
||||
},
|
||||
/** 刷新指定 tab(重置到第一页重新拉取) */
|
||||
refreshTab(index) {
|
||||
const tab = this.tabPages[index]
|
||||
tab.page = 1
|
||||
tab.finished = false
|
||||
this.fetchTab(index, true)
|
||||
},
|
||||
/** 上拉加载下一页 */
|
||||
loadMore(index) {
|
||||
const tab = this.tabPages[index]
|
||||
if (tab.loading || tab.finished) return
|
||||
tab.page += 1
|
||||
this.fetchTab(index, false)
|
||||
},
|
||||
/**
|
||||
* 拉取某个 tab 的列表数据
|
||||
* @param index tab 下标(对应审核状态 0/1/2)
|
||||
* @param isRefresh true=覆盖列表(刷新),false=追加(加载更多)
|
||||
*/
|
||||
async fetchTab(index, isRefresh) {
|
||||
const tab = this.tabPages[index]
|
||||
if (tab.loading) return
|
||||
tab.loading = true
|
||||
let success = false
|
||||
try {
|
||||
const res = await getExamineList({
|
||||
status: tab.status,
|
||||
page: tab.page,
|
||||
pageSize: 20,
|
||||
})
|
||||
const { ok, payload, message } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
const items = ((payload && payload.items) || []).map(this.normalizeItem)
|
||||
tab.list = isRefresh ? items : tab.list.concat(items)
|
||||
// 已到最后一页则停止加载更多
|
||||
tab.finished = tab.page >= ((payload && payload.page_count) || 1)
|
||||
success = true
|
||||
} else {
|
||||
this.$toast(message || '获取列表失败')
|
||||
if (isRefresh) tab.list = []
|
||||
}
|
||||
} catch (e) {
|
||||
this.$toast('网络异常,请稍后重试')
|
||||
}
|
||||
// 加载更多失败:回滚页码,避免重试时跳过一页数据
|
||||
if (!success && !isRefresh) {
|
||||
tab.page = Math.max(1, tab.page - 1)
|
||||
}
|
||||
tab.loading = false
|
||||
// 仅成功才标记已加载:失败的 tab 切走再切回可自动重试首屏
|
||||
if (success) tab.loaded = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 列表项归一化:模板禁止方法调用/可选链,展示字段全部在这里算好
|
||||
* created_at 后端查询器已格式化为字符串,截取到分钟即可
|
||||
*/
|
||||
normalizeItem(it) {
|
||||
const patients = it.user_patient || {}
|
||||
return {
|
||||
id: it.id,
|
||||
prescription_no: it.prescription_no,
|
||||
name: patients.name || '',
|
||||
meta: ((patients.sex || 0) % 2 == 0 ? '女' : '男') + ' · ' + (patients.age || '-') + '岁',
|
||||
time: (it.created_at || '').slice(0, 16),
|
||||
tag: this.prescriptionTypeTag(it.prescription_type),
|
||||
}
|
||||
},
|
||||
/** 处方类型标签:1中药 2西药 3颗粒,颜色与老版 u-tag 对应 */
|
||||
prescriptionTypeTag(type) {
|
||||
const t = Number(type)
|
||||
if (t === 1) return { text: '中药', type: 'success' }
|
||||
if (t === 2) return { text: '西药', type: 'primary' }
|
||||
return { text: '颗粒', type: 'warning' }
|
||||
},
|
||||
/** 底部 tabbar 切换:我的 */
|
||||
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
|
||||
})
|
||||
},
|
||||
|
||||
/** 入驻状态未通过时刷新登录态(走老 Yii user/info,与登录体系一致) */
|
||||
getKnow() {
|
||||
const data = {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
@@ -222,22 +245,6 @@
|
||||
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>
|
||||
@@ -250,13 +257,27 @@
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* 页面纵向 flex 布局:navbar / tabs 占固定高度,swiper 吃满剩余空间(swiper 必须有确定高度才能滚动) */
|
||||
.page-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.head {
|
||||
background-color: #fff;
|
||||
width: 750rpx;
|
||||
height: 99rpx;
|
||||
position: sticky;
|
||||
top: 180rpx;
|
||||
z-index: 9999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-swiper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.tab-scroll {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty {
|
||||
@@ -271,62 +292,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lists {
|
||||
width: 750rpx;
|
||||
padding: 24rpx 36rpx;
|
||||
background: #FFFFFF;
|
||||
margin: 30rpx auto 20rpx;
|
||||
/* 各 tab 内空态:尺寸比整页空态略小,居中展示 */
|
||||
.tab-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
padding: 120rpx 0;
|
||||
|
||||
.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;
|
||||
}
|
||||
image {
|
||||
width: 360rpx;
|
||||
height: 320rpx;
|
||||
}
|
||||
|
||||
.states {
|
||||
font-weight: 400;
|
||||
color: #6A6DCD;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tag_states {
|
||||
margin-bottom: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.wtg {
|
||||
color: #FF477E;
|
||||
}
|
||||
|
||||
.tg {
|
||||
color: #6ACDBB;
|
||||
text {
|
||||
margin-top: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #A7ABB0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
/* 分页脚标:加载中 / 没有更多 */
|
||||
.load-more {
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #A7ABB0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 列表容器:灰底页统一 24rpx 横向留白,底部避开固定 tabbar */
|
||||
.list-wrap {
|
||||
padding: 24rpx 24rpx 140rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -63,8 +63,6 @@
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// this.$go('../../subPackages/sub_workbench/workbench_recipe/index')
|
||||
// console.log(1);
|
||||
this.screenHeight = uni.getSystemInfoSync().windowHeight
|
||||
},
|
||||
onShow() {
|
||||
|
||||
@@ -238,126 +238,120 @@
|
||||
@refresherrefresh="page=1;status = 'loading';loading = true;infoList=[];$store.dispatch('getDocSession')"
|
||||
:refresher-triggered="loading" :refresher-enabled="false" v-if="!loading">
|
||||
|
||||
<!-- 系统消息 -->
|
||||
<view class="news_item flex-row flex-jus-sp flex-ali-center"
|
||||
<!-- 系统消息(有未读):flat-card 分层结构 + 右箭头 -->
|
||||
<view class="news_item flat-card flex-row flex-ali-start"
|
||||
@click="$go('../../subPackages/sub_patient/patient_label?type=1')" v-if="systemList.num>0">
|
||||
<view style="width: 80rpx;position: relative;">
|
||||
<view class="wb-avatar-wrap">
|
||||
<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>
|
||||
<u-badge :count="systemList.num" v-if="systemList.num>0" :offset="[-4,-4]"
|
||||
bgColor="#F44336" :overflow-count="99"></u-badge>
|
||||
</view>
|
||||
<view class="news_item_info flex-col flex-jus-sp">
|
||||
<view class="wb-card-body flex-col flex-1 text-hide">
|
||||
<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>
|
||||
<text class="wb-name">系统消息</text>
|
||||
<text class="wb-time">{{ systemList.time }}</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 class="wb-preview text-hide m-t-8">
|
||||
<text>你有{{systemList.num}}条未读消息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wb-entry-arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 患者消息 -->
|
||||
<view class="news_item flex-row flex-jus-sp flex-ali-center"
|
||||
<!-- 患者消息(有待接诊):flat-card 分层结构 + 右箭头 -->
|
||||
<view class="news_item flat-card flex-row flex-ali-start"
|
||||
@click="$go('../../subPackages/sub_patient/patient_label?type=2')" v-if="infoList.num>0">
|
||||
<view style="width: 80rpx;position: relative;">
|
||||
<view class="wb-avatar-wrap">
|
||||
<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>
|
||||
<u-badge :count="infoList.num" v-if="infoList.num>0" :offset="[-4,-4]" bgColor="#F44336"
|
||||
:overflow-count="99"></u-badge>
|
||||
</view>
|
||||
<view class="news_item_info flex-col flex-jus-sp">
|
||||
<view class="wb-card-body flex-col flex-1 text-hide">
|
||||
<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>
|
||||
<text class="wb-name">患者消息</text>
|
||||
<text class="wb-time">{{ infoList.time }}</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 class="wb-preview text-hide m-t-8">
|
||||
<text>你有{{infoList.num}}名患者待接诊</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wb-entry-arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="news_item flex-row flex-jus-sp flex-ali-center"
|
||||
<!-- 系统消息(无未读兜底入口) -->
|
||||
<view class="news_item flat-card flex-row flex-ali-start"
|
||||
v-if="!loading&&systemList.length==0"
|
||||
@click="$go('../../subPackages/sub_patient/patient_label?type=1')">
|
||||
<view style="width: 80rpx;position: relative;">
|
||||
<view class="wb-avatar-wrap">
|
||||
<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="wb-card-body flex-col flex-1 text-hide">
|
||||
<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>
|
||||
<text class="wb-name">系统消息</text>
|
||||
<text class="wb-time">{{ systemList.time }}</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 class="wb-preview text-hide m-t-8">
|
||||
<text>点击查看历史消息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wb-entry-arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 历史患者 -->
|
||||
<view class="news_item flex-row flex-jus-sp flex-ali-center" v-if="!loading&&infoList.length==0"
|
||||
<!-- 患者消息(无待接诊兜底入口) -->
|
||||
<view class="news_item flat-card flex-row flex-ali-start" v-if="!loading&&infoList.length==0"
|
||||
@click="$go('../../subPackages/sub_patient/patient_label?type=2')">
|
||||
<view style="width: 80rpx;position: relative;">
|
||||
<view class="wb-avatar-wrap">
|
||||
<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="wb-card-body flex-col flex-1 text-hide">
|
||||
<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>
|
||||
<text class="wb-name">患者消息</text>
|
||||
<text class="wb-time">{{ infoList.time }}</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 class="wb-preview text-hide m-t-8">
|
||||
<text>点击查看历史消息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wb-entry-arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 处方 -->
|
||||
<view v-if="noticeList.length" class="news_item flex-row flex-jus-sp flex-ali-center"
|
||||
<!-- 待流转处方 -->
|
||||
<view v-if="noticeList.length" class="news_item flat-card flex-row flex-ali-start"
|
||||
@click="$go('/subPackages/sub_workbench/prescriptionList')">
|
||||
<view style="width: 80rpx;position: relative;">
|
||||
<view class="wb-avatar-wrap">
|
||||
<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>
|
||||
<u-badge :count="noticeList.length" :offset="[-4,-4]"
|
||||
bgColor="#F44336" :overflow-count="99"></u-badge>
|
||||
</view>
|
||||
<view class="news_item_info flex-col flex-jus-sp">
|
||||
<view class="wb-card-body flex-col flex-1 text-hide">
|
||||
<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>
|
||||
<text class="wb-name">待流转处方消息</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;">你有{{noticeList.length}}条未读消息</text>
|
||||
</view>
|
||||
<view class="wb-preview text-hide m-t-8">
|
||||
<text>你有{{noticeList.length}}条未读消息</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wb-entry-arrow">
|
||||
<u-icon name="arrow-right" color="#C0C4CC" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
@@ -1463,6 +1457,13 @@ page {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 消息入口行右侧箭头:垂直居中,暗示整行可点 */
|
||||
.wb-entry-arrow {
|
||||
flex-shrink: 0;
|
||||
margin-left: 12rpx;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.wb-action-btn {
|
||||
padding: 10rpx 28rpx;
|
||||
font-size: 24rpx;
|
||||
|
||||
136
pages1.json
136
pages1.json
@@ -1,136 +0,0 @@
|
||||
{
|
||||
"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": "#666666",
|
||||
"selectedColor": "#1575FC",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "black",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<template>
|
||||
<web-view v-if="src" @message="getMessage" ref="webview" :src="src">
|
||||
</web-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
src: "",
|
||||
};
|
||||
},
|
||||
onLoad(op) {
|
||||
const userInfo = uni.getStorageSync("userInfo");
|
||||
this.src = `https://wap.nbxlmy.com?name=${userInfo.name}&name2=${op.name2}&avatar=${userInfo.avatar}&type=1&userID=${op.userId}&SDKAppID=${op.appid}&myUserID=${op.myUserId}&userSig=${op.key}&avatar2=${op.avatarurl}`;
|
||||
console.log(this.src, " this.src");
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.$refs);
|
||||
},
|
||||
methods: {
|
||||
getMessage(e) {
|
||||
if (e) {
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
278
subPackages/sub_business_shared/card-style/index.vue
Normal file
278
subPackages/sub_business_shared/card-style/index.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<!--
|
||||
工作台卡片样式选择页(四个业务角色端共用,跨分包 navigateTo 进入)
|
||||
两张 CSS 手绘预览卡单选:长卡片(一行两张 iOS 行卡)/ 宫格(金刚区),点击即持久化,
|
||||
返回工作台 onShow 重读生效;「我的」页入口行通过 uni.$emit 同步文案
|
||||
-->
|
||||
<view class="cs-page">
|
||||
<u-navbar
|
||||
title="卡片样式"
|
||||
:is-back="true"
|
||||
title-size="34"
|
||||
title-color="#fff"
|
||||
back-icon-color="#fff"
|
||||
:background="{ background: 'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)' }"
|
||||
></u-navbar>
|
||||
<view class="cs-body">
|
||||
<view class="cs-tip">选择工作台功能入口的展示样式,选中立即生效</view>
|
||||
|
||||
<!-- 选项一:长卡片(一行两张 iOS 行卡示意,2×2 排布) -->
|
||||
<view class="style-option" :class="{ active: style === 'long' }" hover-class="option-hover" @tap="choose('long')">
|
||||
<view class="preview">
|
||||
<view class="mock-row-grid">
|
||||
<view class="mock-row">
|
||||
<view class="mock-tile tile-a"></view>
|
||||
<view class="mock-lines">
|
||||
<view class="mock-line-main"></view>
|
||||
<view class="mock-line-sub"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mock-row">
|
||||
<view class="mock-tile tile-b"></view>
|
||||
<view class="mock-lines">
|
||||
<view class="mock-line-main"></view>
|
||||
<view class="mock-line-sub"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mock-row">
|
||||
<view class="mock-tile tile-c"></view>
|
||||
<view class="mock-lines">
|
||||
<view class="mock-line-main"></view>
|
||||
<view class="mock-line-sub"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mock-row">
|
||||
<view class="mock-tile tile-d"></view>
|
||||
<view class="mock-lines">
|
||||
<view class="mock-line-main"></view>
|
||||
<view class="mock-line-sub"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="option-meta">
|
||||
<view class="option-text">
|
||||
<text class="option-name">长卡片</text>
|
||||
<text class="option-desc">一行两张大卡,带功能描述,信息更完整</text>
|
||||
</view>
|
||||
<view class="option-check" :class="{ checked: style === 'long' }">
|
||||
<u-icon v-if="style === 'long'" name="checkmark" size="26" color="#FFFFFF"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选项二:宫格(金刚区示意) -->
|
||||
<view class="style-option" :class="{ active: style === 'short' }" hover-class="option-hover" @tap="choose('short')">
|
||||
<view class="preview">
|
||||
<view class="mock-grid">
|
||||
<view class="mock-cell">
|
||||
<view class="mock-tile tile-grid tile-a"></view>
|
||||
<view class="mock-label"></view>
|
||||
</view>
|
||||
<view class="mock-cell">
|
||||
<view class="mock-tile tile-grid tile-b"></view>
|
||||
<view class="mock-label"></view>
|
||||
</view>
|
||||
<view class="mock-cell">
|
||||
<view class="mock-tile tile-grid tile-c"></view>
|
||||
<view class="mock-label"></view>
|
||||
</view>
|
||||
<view class="mock-cell">
|
||||
<view class="mock-tile tile-grid tile-d"></view>
|
||||
<view class="mock-label"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="option-meta">
|
||||
<view class="option-text">
|
||||
<text class="option-name">宫格</text>
|
||||
<text class="option-desc">金刚区图标排列,一屏看到更多功能</text>
|
||||
</view>
|
||||
<view class="option-check" :class="{ checked: style === 'short' }">
|
||||
<u-icon v-if="style === 'short'" name="checkmark" size="26" color="#FFFFFF"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 卡片样式选择页
|
||||
* 存储沿用 utils/workbenchCardPref.js 的 wb_card_style 键(long / short),
|
||||
* short 的渲染含义为金刚区宫格(四首页与本页文案保持一致)。
|
||||
* 为什么放 sub_business_shared:四端「我的」页都要进入,共享分包页面可被任意分包 navigateTo。
|
||||
*/
|
||||
import { getCardStyle, setCardStyle } from '@/utils/workbenchCardPref.js';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
/** 当前选中样式:long 长卡片 / short 宫格 */
|
||||
style: 'short',
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.style = getCardStyle();
|
||||
},
|
||||
methods: {
|
||||
/** 点击预览卡即选中并持久化;广播给「我的」页入口行同步文案 */
|
||||
choose(style) {
|
||||
if (this.style === style) return;
|
||||
this.style = style;
|
||||
setCardStyle(style);
|
||||
uni.$emit('wbCardStyleChanged', style);
|
||||
uni.showToast({ title: style === 'long' ? '已切换为长卡片' : '已切换为宫格', icon: 'none' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.cs-body {
|
||||
padding: 24rpx 32rpx 60rpx;
|
||||
}
|
||||
.cs-tip {
|
||||
font-size: 24rpx;
|
||||
color: #8E8E93;
|
||||
padding: 8rpx 6rpx 24rpx;
|
||||
}
|
||||
/* 选项卡片:iOS 卡片规格,选中态主题色描边 + 轻主题底 */
|
||||
.style-option {
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
border: 4rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
.style-option.active {
|
||||
border-color: #6ACDBB;
|
||||
background: rgba(106, 205, 187, 0.04);
|
||||
}
|
||||
.option-hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
/* 预览区:浅灰底衬托手绘示意 */
|
||||
.preview {
|
||||
background: #F4F6F8;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
}
|
||||
/* --- 长卡片示意:2×2 排布的行卡(磁贴 + 两行文字线),对应真实的一行两张大卡 --- */
|
||||
.mock-row-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14rpx;
|
||||
}
|
||||
.mock-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.04);
|
||||
min-width: 0;
|
||||
}
|
||||
.mock-tile {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tile-a { background: #6ACDBB; }
|
||||
.tile-b { background: #3B82F6; }
|
||||
.tile-c { background: #F97316; }
|
||||
.tile-d { background: #A855F7; }
|
||||
.mock-lines {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
.mock-line-main {
|
||||
width: 55%;
|
||||
height: 14rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #D8DCE1;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.mock-line-sub {
|
||||
width: 80%;
|
||||
height: 10rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #EBEEF2;
|
||||
}
|
||||
/* --- 宫格示意:白卡内 4 列磁贴 + 标签线 --- */
|
||||
.mock-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
padding: 20rpx 8rpx;
|
||||
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.mock-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.mock-tile.tile-grid {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.mock-label {
|
||||
width: 56rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 6rpx;
|
||||
background: #EBEEF2;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
/* --- 选项文字与选中对勾 --- */
|
||||
.option-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.option-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.option-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1C1C1E;
|
||||
}
|
||||
.option-desc {
|
||||
font-size: 24rpx;
|
||||
color: #8E8E93;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
/* 圆形对勾:未选中空描边圆,选中主题色实心 + 白勾 */
|
||||
.option-check {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
border: 3rpx solid #D8DCE1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-left: 20rpx;
|
||||
transition: background 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
.option-check.checked {
|
||||
background: #6ACDBB;
|
||||
border-color: #6ACDBB;
|
||||
}
|
||||
</style>
|
||||
@@ -30,6 +30,7 @@ export function getPlatformAdminContext() {
|
||||
},
|
||||
tabList: [
|
||||
{ text: '工作台', iconPath: 'home', selectedIconPath: 'home-fill', pagePath: PLATFORM_HOME },
|
||||
{ text: '功能', iconPath: 'grid', selectedIconPath: 'grid-fill', pagePath: '/subPackages/sub_platform_admin/features/index' },
|
||||
{ text: '我的', iconPath: 'account', selectedIconPath: 'account-fill', pagePath: PLATFORM_MY },
|
||||
],
|
||||
};
|
||||
@@ -54,6 +55,7 @@ export function getSalespersonContext() {
|
||||
},
|
||||
tabList: [
|
||||
{ text: '工作台', iconPath: 'home', selectedIconPath: 'home-fill', pagePath: SALESPERSON_HOME },
|
||||
{ text: '功能', iconPath: 'grid', selectedIconPath: 'grid-fill', pagePath: '/subPackages/sub_salesperson/features/index' },
|
||||
{ text: '我的', iconPath: 'account', selectedIconPath: 'account-fill', pagePath: SALESPERSON_MY },
|
||||
],
|
||||
};
|
||||
@@ -80,6 +82,7 @@ export function getClinicAdminContext() {
|
||||
},
|
||||
tabList: [
|
||||
{ text: '工作台', iconPath: 'home', selectedIconPath: 'home-fill', pagePath: '/subPackages/sub_clinic_admin/home/index' },
|
||||
{ text: '功能', iconPath: 'grid', selectedIconPath: 'grid-fill', pagePath: '/subPackages/sub_clinic_admin/features/index' },
|
||||
{ text: '我的', iconPath: 'account', selectedIconPath: 'account-fill', pagePath: '/subPackages/sub_clinic_admin/my/index' },
|
||||
],
|
||||
};
|
||||
|
||||
80
subPackages/sub_business_shared/common/profileContext.js
Normal file
80
subPackages/sub_business_shared/common/profileContext.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 个人中心多端上下文(sub_business_shared/profile 专用)
|
||||
* 为什么单独建而不是扩展 resolveBusinessContext:现有 resolveBusinessContext 只覆盖
|
||||
* 平台管理员/业务员/诊所管理员 3 端且服务于订单等业务页,这里需要覆盖含诊所推广员的
|
||||
* 4 端并聚焦「个人资料」能力,独立文件避免影响既有业务页
|
||||
* 四端接口均为 xk-api(jok 形态),API 封装统一返回 { res, data, ok }
|
||||
*/
|
||||
import {
|
||||
getClinicAdminMyProfile,
|
||||
updateClinicAdminMyProfile,
|
||||
updateClinicAdminMyPassword,
|
||||
} from '@/api/clinicAdmin.js';
|
||||
import { platformAdminApi, salespersonApi } from '@/api/businessApi.js';
|
||||
import {
|
||||
getClinicSalespersonMyProfile,
|
||||
updateClinicSalespersonMyProfile,
|
||||
updateClinicSalespersonMyPassword,
|
||||
} from '@/api/clinicSalesperson.js';
|
||||
|
||||
const CONTEXT_MAP = {
|
||||
clinic_admin: {
|
||||
mode: 'clinic_admin',
|
||||
roleFallback: '诊所管理员',
|
||||
userStorageKey: 'clinic_admin_user',
|
||||
getMyProfile: getClinicAdminMyProfile,
|
||||
updateMyProfile: updateClinicAdminMyProfile,
|
||||
updateMyPassword: updateClinicAdminMyPassword,
|
||||
},
|
||||
platform_admin: {
|
||||
mode: 'platform_admin',
|
||||
roleFallback: '平台管理员',
|
||||
userStorageKey: 'platform_admin_user',
|
||||
getMyProfile: () => platformAdminApi.getMyProfile(),
|
||||
updateMyProfile: (data) => platformAdminApi.updateMyProfile(data),
|
||||
updateMyPassword: (data) => platformAdminApi.updateMyPassword(data),
|
||||
},
|
||||
salesperson: {
|
||||
mode: 'salesperson',
|
||||
roleFallback: '业务员',
|
||||
userStorageKey: 'salesperson_user',
|
||||
getMyProfile: () => salespersonApi.getMyProfile(),
|
||||
updateMyProfile: (data) => salespersonApi.updateMyProfile(data),
|
||||
updateMyPassword: (data) => salespersonApi.updateMyPassword(data),
|
||||
},
|
||||
clinic_salesperson: {
|
||||
mode: 'clinic_salesperson',
|
||||
roleFallback: '推广员',
|
||||
userStorageKey: 'clinic_salesperson_user',
|
||||
getMyProfile: getClinicSalespersonMyProfile,
|
||||
updateMyProfile: updateClinicSalespersonMyProfile,
|
||||
updateMyPassword: updateClinicSalespersonMyPassword,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 按当前 loginMode 解析个人中心上下文(默认诊所管理员,避免异常 mode 导致白屏)
|
||||
*/
|
||||
export function resolveProfileContext() {
|
||||
const mode = uni.getStorageSync('loginMode');
|
||||
return CONTEXT_MAP[mode] || CONTEXT_MAP.clinic_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资料保存成功后把昵称/头像写回各端 user 缓存
|
||||
* 为什么要写回:四端「我的」页 onShow 先读本地缓存兜底首屏,不写回的话返回时会闪旧资料
|
||||
* 注意推广员端缓存是 { salesperson, store } 嵌套结构,与其余三端平铺结构不同
|
||||
* @param {object} ctx resolveProfileContext 返回的上下文
|
||||
* @param {object} profile 最新资料(至少含 nick_name / avatar)
|
||||
*/
|
||||
export function applyProfileToCache(ctx, profile) {
|
||||
if (!ctx || !profile) return;
|
||||
const cached = uni.getStorageSync(ctx.userStorageKey) || {};
|
||||
const patch = { nick_name: profile.nick_name || '', avatar: profile.avatar || '' };
|
||||
if (ctx.mode === 'clinic_salesperson') {
|
||||
cached.salesperson = Object.assign({}, cached.salesperson || {}, patch);
|
||||
uni.setStorageSync(ctx.userStorageKey, cached);
|
||||
return;
|
||||
}
|
||||
uni.setStorageSync(ctx.userStorageKey, Object.assign({}, cached, patch));
|
||||
}
|
||||
@@ -1,22 +1,38 @@
|
||||
/**
|
||||
* 诊所管理员端 tabbar 配置(sub_business_shared/clinic-page-layout 用的副本)
|
||||
* 与 sub_clinic_admin/common/tabbar.js 保持同步:三 tab(工作台/功能/我的)+ uView 图标名
|
||||
*/
|
||||
export const TAB_WORKBENCH = 0
|
||||
export const TAB_MY = 1
|
||||
export const TAB_FEATURES = 1
|
||||
export const TAB_MY = 2
|
||||
|
||||
const ROOT = '/subPackages/sub_clinic_admin'
|
||||
|
||||
export const CLINIC_TAB_ROUTES = [
|
||||
'/subPackages/sub_clinic_admin/home/index',
|
||||
'/subPackages/sub_clinic_admin/my/index',
|
||||
`${ROOT}/home/index`,
|
||||
`${ROOT}/features/index`,
|
||||
`${ROOT}/my/index`,
|
||||
]
|
||||
|
||||
export function getClinicTabList() {
|
||||
return [
|
||||
{
|
||||
iconPath: require('@/static/image/w-sf.png'),
|
||||
selectedIconPath: require('@/static/image/sf.png'),
|
||||
iconPath: 'home',
|
||||
selectedIconPath: 'home-fill',
|
||||
text: '工作台',
|
||||
pagePath: `${ROOT}/home/index`,
|
||||
},
|
||||
{
|
||||
iconPath: require('@/static/image/w-wd.png'),
|
||||
selectedIconPath: require('@/static/image/wd.png'),
|
||||
iconPath: 'grid',
|
||||
selectedIconPath: 'grid-fill',
|
||||
text: '功能',
|
||||
pagePath: `${ROOT}/features/index`,
|
||||
},
|
||||
{
|
||||
iconPath: 'account',
|
||||
selectedIconPath: 'account-fill',
|
||||
text: '我的',
|
||||
pagePath: `${ROOT}/my/index`,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,16 +4,24 @@
|
||||
<view v-if="!auditMode" class="toolbar">
|
||||
<button class="btn" @click="goForm()">新建录入</button>
|
||||
</view>
|
||||
<view v-for="item in list" :key="item.id" class="card" @click="goDetail(item)">
|
||||
<view class="row">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="status" :class="'s' + item.status">{{ statusText(item.status) }}</text>
|
||||
</view>
|
||||
<view class="sub">{{ item.mobile }} · {{ formatTime(item.created_at) }}</view>
|
||||
<view v-if="inputType === 'store' && item.promoter_admin" class="promoter-row" @click.stop>
|
||||
<promoter-info-card :admin="item.promoter_admin" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 录入列表卡:统一 xk-list-card 规范;门店卡附带推广员信息(body 插槽) -->
|
||||
<xk-list-card
|
||||
v-for="item in list" :key="item.id"
|
||||
:avatar-text="item.name"
|
||||
:title="item.name"
|
||||
:time="formatTime(item.created_at)"
|
||||
:status="{ text: statusText(item.status), color: statusColor(item.status) }"
|
||||
:actions="[{ key: 'detail', text: '查看详情', type: 'ghost' }]"
|
||||
@action="goDetail(item)"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<template #body>
|
||||
<text class="card-sub">{{ item.mobile || '-' }}</text>
|
||||
<view v-if="inputType === 'store' && item.promoter_admin" class="promoter-row" @tap.stop>
|
||||
<promoter-info-card :admin="item.promoter_admin" />
|
||||
</view>
|
||||
</template>
|
||||
</xk-list-card>
|
||||
<view v-if="!list.length" class="empty">暂无记录</view>
|
||||
</view>
|
||||
</business-page-layout>
|
||||
@@ -61,6 +69,10 @@ export default {
|
||||
statusText(s) {
|
||||
return { 0: '待审核', 1: '已通过', 2: '已拒绝' }[Number(s)] || '-'
|
||||
},
|
||||
/** 状态色映射:与 statusText 对应(橙=待审核 / 绿=通过 / 红=拒绝) */
|
||||
statusColor(s) {
|
||||
return { 0: '#FF7D00', 1: '#00B42A', 2: '#F53F3F' }[Number(s)] || '#999'
|
||||
},
|
||||
formatTime(t) {
|
||||
if (!t) return '-'
|
||||
const d = new Date(Number(t) * 1000)
|
||||
@@ -78,14 +90,8 @@ export default {
|
||||
.page-wrap { padding: 24rpx; }
|
||||
.toolbar { margin-bottom: 24rpx; }
|
||||
.btn { background: #6acdbb; color: #fff; font-size: 28rpx; }
|
||||
.card { background: #fff; padding: 28rpx; border-radius: 16rpx; margin-bottom: 16rpx; }
|
||||
.row { display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { font-size: 30rpx; font-weight: 600; }
|
||||
.status { font-size: 24rpx; }
|
||||
.s0 { color: #FF7D00; }
|
||||
.s1 { color: #00B42A; }
|
||||
.s2 { color: #F53F3F; }
|
||||
.sub { margin-top: 12rpx; color: #86909C; font-size: 24rpx; }
|
||||
/* 卡内副文案行(body 插槽内自带样式,组件 rows 样式不作用于插槽) */
|
||||
.card-sub { display: block; margin-top: 8rpx; color: #86909C; font-size: 24rpx; }
|
||||
.promoter-row { margin-top: 16rpx; padding-top: 12rpx; border-top: 1rpx solid #f5f5f5; }
|
||||
.empty { text-align: center; color: #86909C; padding: 80rpx 0; }
|
||||
</style>
|
||||
|
||||
@@ -88,9 +88,13 @@ $color-text-body: #4E5969;
|
||||
$color-text-muted: #86909C;
|
||||
$color-border: #F2F3F5;
|
||||
|
||||
/* iOS 卡片规格:与订单详情各面板一致(大圆角 + 发丝线 + 轻阴影) */
|
||||
.express-panel {
|
||||
background: #ffffff;
|
||||
padding: 32rpx 40rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,24 @@
|
||||
<view>
|
||||
<business-page-layout title="订单详情" :show-back="true">
|
||||
<view class="detail-container">
|
||||
<!-- 头部面板:通栏,展示金额与状态 -->
|
||||
<!-- 状态头卡:状态大字(状态色区分)+ 实付金额 + 可复制单号 -->
|
||||
<view v-if="info.id" class="detail-panel header-panel">
|
||||
<view class="order-head">
|
||||
<view class="status-head">
|
||||
<view class="status-main">
|
||||
<view class="status-dot" :style="{ background: statusColor }"></view>
|
||||
<text class="status-text" :style="{ color: statusColor }">{{ detailExtra.statusText }}</text>
|
||||
</view>
|
||||
<view class="pay-group">
|
||||
<text class="pay-label">实付</text>
|
||||
<text class="pay-total">{{ money(info.total_pay_price) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-no-row">
|
||||
<text class="order-no-main">订单号:{{ info.order_no }}</text>
|
||||
<text class="pay-total">{{ money(info.total_pay_price) }}</text>
|
||||
<view class="copy-btn" hover-class="copy-btn-hover" @tap="copyOrderNo"><text>复制</text></view>
|
||||
</view>
|
||||
|
||||
<view class="tags">
|
||||
<text class="modern-tag tag-status">{{ detailExtra.statusText }}</text>
|
||||
<text class="modern-tag tag-type">{{ detailExtra.prescriptionTypeText }}</text>
|
||||
<text class="modern-tag tag-delivery">{{ detailExtra.deliveryText }}</text>
|
||||
<text v-if="detailExtra.isProxyPay" class="modern-tag tag-proxy">代支付</text>
|
||||
@@ -309,11 +318,29 @@ export default {
|
||||
canViewErpMenu() {
|
||||
return this.canViewErp && Number(this.info.prescription_type) === 1
|
||||
},
|
||||
/** 订单状态色:待办橙 / 在途蓝 / 完成绿 / 退款红 / 取消灰(与列表状态语义一致) */
|
||||
statusColor() {
|
||||
const m = {
|
||||
0: '#FF7D00', 1: '#FF7D00', 2: '#3B82F6', 3: '#00B42A', 4: '#F53F3F',
|
||||
5: '#FF7D00', 6: '#00B42A', 7: '#00B42A', 8: '#F53F3F', 9: '#86909C',
|
||||
}
|
||||
return m[Number(this.info.status)] || '#86909C'
|
||||
},
|
||||
},
|
||||
onLoad(q) {
|
||||
this.id = Number(q.id || 0)
|
||||
this.load()
|
||||
},
|
||||
/** 下拉刷新:重拉详情(含物流),完成后收起刷新动画 */
|
||||
onPullDownRefresh() {
|
||||
const done = () => uni.stopPullDownRefresh()
|
||||
const p = this.load()
|
||||
if (p && p.then) {
|
||||
p.then(done, done)
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
},
|
||||
/** 离开页关闭处方抽屉,避免 page-container 残留遮罩导致列表白屏 */
|
||||
onHide() {
|
||||
this.closePrescriptionPopup()
|
||||
@@ -344,12 +371,13 @@ export default {
|
||||
this.patientDetailName = this.info.patient || ''
|
||||
this.patientDetailVisible = true
|
||||
},
|
||||
/** 拉取订单详情;返回 Promise 供下拉刷新在完成后收起动画 */
|
||||
load() {
|
||||
if (!this.id) {
|
||||
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.bizApi.getOrderDetail(this.id).then(w => {
|
||||
return this.bizApi.getOrderDetail(this.id).then(w => {
|
||||
if (!w.ok) {
|
||||
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||
return
|
||||
@@ -364,6 +392,13 @@ export default {
|
||||
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
/** 复制订单号(状态头卡复制按钮) */
|
||||
copyOrderNo() {
|
||||
uni.setClipboardData({
|
||||
data: String(this.info.order_no || ''),
|
||||
success: () => uni.showToast({ title: '订单号已复制', icon: 'none' }),
|
||||
})
|
||||
},
|
||||
loadExpress() {
|
||||
this.bizApi.getExpressDetailByOrderId(this.id).then(w => {
|
||||
if (w.ok) this.expressInfo = w.data
|
||||
@@ -435,48 +470,110 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$color-page-bg: #F5F7F8;
|
||||
$color-text-title: #222B2A;
|
||||
$color-page-bg: #F4F6F8;
|
||||
$color-text-title: #1C1C1E;
|
||||
$color-text-body: #4E5969;
|
||||
$color-text-muted: #86909C;
|
||||
$color-text-muted: #8E8E93;
|
||||
$color-border: #F2F3F5;
|
||||
$color-danger: #F53F3F;
|
||||
|
||||
.detail-container {
|
||||
background-color: $color-page-bg;
|
||||
min-height: 100vh;
|
||||
/* 适配底部操作栏,留出极大的安全边距 */
|
||||
padding-bottom: 180rpx;
|
||||
/* iOS 卡片流:两侧留白 + 适配底部操作栏的安全边距 */
|
||||
padding: 20rpx 24rpx 180rpx;
|
||||
}
|
||||
|
||||
/* 通栏面板 */
|
||||
/* iOS 卡片面板:大圆角 + 发丝线 + 轻阴影(与工作台卡片同规格) */
|
||||
.detail-panel {
|
||||
background: #ffffff;
|
||||
padding: 32rpx 40rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.header-panel {
|
||||
padding-top: 40rpx;
|
||||
padding-top: 32rpx;
|
||||
}
|
||||
|
||||
.order-head {
|
||||
/* 状态头:左状态大字(色点 + 文案),右实付金额 */
|
||||
.status-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-no-main {
|
||||
font-size: 28rpx;
|
||||
.status-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pay-group {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.pay-label {
|
||||
font-size: 22rpx;
|
||||
color: $color-text-muted;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
/* 单号行:灰底圆角条 + 复制按钮 */
|
||||
.order-no-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: rgba(60, 60, 67, 0.04);
|
||||
border-radius: 16rpx;
|
||||
padding: 14rpx 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.order-no-main {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-muted;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
padding: 4rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid $theme-primary;
|
||||
text {
|
||||
font-size: 22rpx;
|
||||
color: $theme-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-btn-hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pay-total {
|
||||
font-size: 40rpx;
|
||||
color: $color-text-title;
|
||||
font-weight: 700;
|
||||
font-family: monospace, sans-serif;
|
||||
font-family: "DIN Alternate", monospace, sans-serif;
|
||||
}
|
||||
|
||||
.tags {
|
||||
@@ -493,7 +590,6 @@ $color-danger: #F53F3F;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-status { background: rgba(106, 205, 187, 0.1); color: $theme-primary; }
|
||||
.tag-type { background: rgba(230, 162, 60, 0.1); color: #e6a23c; }
|
||||
.tag-delivery { background: rgba(91, 127, 214, 0.1); color: #5b7fd6; }
|
||||
.tag-proxy { background: rgba(114, 46, 209, 0.1); color: #722ED1; }
|
||||
@@ -678,6 +774,7 @@ $color-danger: #F53F3F;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
padding: 24rpx 40rpx;
|
||||
/* 自动撑起 iOS 底部安全距离 */
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
|
||||
416
subPackages/sub_business_shared/profile/index.vue
Normal file
416
subPackages/sub_business_shared/profile/index.vue
Normal file
@@ -0,0 +1,416 @@
|
||||
<template>
|
||||
<!--
|
||||
个人中心(四个管理端共用,跨分包 navigateTo 进入)
|
||||
头像/昵称可编辑,账号/手机号/角色/所属门店等只读,支持修改登录密码;
|
||||
按 loginMode 走各端 API(profileContext),保存成功写回各端 user 缓存
|
||||
-->
|
||||
<view class="profile-page">
|
||||
<u-navbar
|
||||
title="个人中心"
|
||||
:is-back="true"
|
||||
title-size="34"
|
||||
title-color="#fff"
|
||||
back-icon-color="#fff"
|
||||
:background="{ background: 'linear-gradient(90deg, #00BFA6 0%, #6ACDBB 93%)' }"
|
||||
></u-navbar>
|
||||
<view class="profile-body">
|
||||
<!-- 可编辑区:头像 + 昵称 -->
|
||||
<view class="panel">
|
||||
<view class="row" hover-class="row-hover" @tap="changeAvatar">
|
||||
<text class="row-label">头像</text>
|
||||
<view class="row-value">
|
||||
<image v-if="profile.avatar" class="avatar-img" :src="profile.avatar" mode="aspectFill" />
|
||||
<view v-else class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="row row-last" hover-class="row-hover" @tap="openNickDrawer">
|
||||
<text class="row-label">昵称</text>
|
||||
<view class="row-value">
|
||||
<text class="value-text">{{ profile.nick_name || '-' }}</text>
|
||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 只读区:账号身份信息不允许自助修改 -->
|
||||
<view class="panel">
|
||||
<view v-for="(item, index) in readonlyRows" :key="item.label" class="row" :class="{ 'row-last': index === readonlyRows.length - 1 }">
|
||||
<text class="row-label">{{ item.label }}</text>
|
||||
<view class="row-value">
|
||||
<text class="value-text value-readonly">{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 安全区:修改密码 -->
|
||||
<view class="panel">
|
||||
<view class="row row-last" hover-class="row-hover" @tap="openPwdDrawer">
|
||||
<text class="row-label">修改密码</text>
|
||||
<view class="row-value">
|
||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 昵称编辑抽屉(page-container 防误退,v-if 由组件内部处理) -->
|
||||
<drawer-page-container v-model="nickDrawerVisible" title="修改昵称">
|
||||
<view class="drawer-form">
|
||||
<input
|
||||
v-model="nickForm.nick_name"
|
||||
class="form-input"
|
||||
type="text"
|
||||
:maxlength="20"
|
||||
placeholder="请输入昵称(最长 20 个字)"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:loading="saving"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '88rpx', marginTop: '40rpx' }"
|
||||
@click="saveNickName"
|
||||
>保存</u-button>
|
||||
</view>
|
||||
</drawer-page-container>
|
||||
<!-- 修改密码抽屉 -->
|
||||
<drawer-page-container v-model="pwdDrawerVisible" title="修改密码">
|
||||
<view class="drawer-form">
|
||||
<view class="pwd-field">
|
||||
<text class="pwd-label">旧密码</text>
|
||||
<input
|
||||
v-model="pwdForm.password"
|
||||
class="form-input"
|
||||
:password="true"
|
||||
placeholder="请输入旧密码"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<view class="pwd-field">
|
||||
<text class="pwd-label">新密码</text>
|
||||
<input
|
||||
v-model="pwdForm.new_password"
|
||||
class="form-input"
|
||||
:password="true"
|
||||
placeholder="请输入新密码(5-18 位)"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<view class="pwd-field">
|
||||
<text class="pwd-label">确认新密码</text>
|
||||
<input
|
||||
v-model="pwdForm.new_password_confirmation"
|
||||
class="form-input"
|
||||
:password="true"
|
||||
placeholder="请再次输入新密码"
|
||||
placeholder-class="input-placeholder"
|
||||
/>
|
||||
</view>
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:loading="saving"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '88rpx', marginTop: '40rpx' }"
|
||||
@click="savePassword"
|
||||
>确认修改</u-button>
|
||||
</view>
|
||||
</drawer-page-container>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 共享个人中心页:按 loginMode 解析四端上下文(API/缓存 key/角色兜底文案)
|
||||
* 头像流程:chooseImage → 压缩 → 内容安全检查 → {end}-upload/image → update-my-profile
|
||||
* 所有接口均为 xk-api(code/result 形态),API 封装已统一解包为 { data, ok }
|
||||
*/
|
||||
import DrawerPageContainer from '@/subPackages/sub_business_shared/components/DrawerPageContainer.vue';
|
||||
import { resolveProfileContext, applyProfileToCache } from '@/subPackages/sub_business_shared/common/profileContext.js';
|
||||
import { uploadProfileAvatarApi } from '@/api/upload.js';
|
||||
import { prepareImagePath } from '@/common/js/image-compress.js';
|
||||
import { checkImage, handleSecurityError, SCENE_PROFILE } from '@/common/js/content-security.js';
|
||||
|
||||
export default {
|
||||
components: { DrawerPageContainer },
|
||||
data() {
|
||||
return {
|
||||
/** 当前端上下文(onLoad 解析一次,页面生命周期内 loginMode 不会变) */
|
||||
ctx: null,
|
||||
/** 个人资料(my-profile 实时接口返回) */
|
||||
profile: {},
|
||||
nickDrawerVisible: false,
|
||||
nickForm: { nick_name: '' },
|
||||
pwdDrawerVisible: false,
|
||||
pwdForm: { password: '', new_password: '', new_password_confirmation: '' },
|
||||
saving: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/** 文字头像兜底:昵称首字 */
|
||||
avatarText() {
|
||||
const name = this.profile.nick_name || '用';
|
||||
return name.slice(0, 1);
|
||||
},
|
||||
/** 只读信息行:可选字段(门店/工号/最后登录)有值才展示 */
|
||||
readonlyRows() {
|
||||
const p = this.profile || {};
|
||||
const rows = [
|
||||
{ label: '登录账号', value: p.login_account || '-' },
|
||||
{ label: '手机号', value: p.phone || '-' },
|
||||
{ label: '角色', value: p.role_name || (this.ctx && this.ctx.roleFallback) || '-' },
|
||||
];
|
||||
if (p.store_name) rows.push({ label: '所属门店', value: p.store_name });
|
||||
if (p.job_number) rows.push({ label: '工号', value: p.job_number });
|
||||
if (p.last_login_time) rows.push({ label: '最后登录', value: p.last_login_time });
|
||||
return rows;
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.ctx = resolveProfileContext();
|
||||
this.loadProfile();
|
||||
},
|
||||
methods: {
|
||||
/** 拉取实时资料(xk-api my-profile,封装已解包 { data, ok };失败静默保留空态) */
|
||||
async loadProfile() {
|
||||
try {
|
||||
const { data, ok } = await this.ctx.getMyProfile();
|
||||
if (ok && data) {
|
||||
this.profile = data;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('拉取个人资料失败', e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 更换头像:选图 → 压缩(大图省流量)→ 内容安全检查 → 上传各端 image 路由 → 保存资料
|
||||
* 上传成功即调 update-my-profile 落库并刷新 Redis 会话,无需用户再点保存
|
||||
*/
|
||||
changeAvatar() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: async (res) => {
|
||||
uni.showLoading({ title: '上传中...', mask: true });
|
||||
try {
|
||||
const tempFile = res.tempFiles[0];
|
||||
const prepared = await prepareImagePath({ path: tempFile.path, size: tempFile.size });
|
||||
if (!prepared) {
|
||||
uni.hideLoading();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await checkImage(prepared.path, SCENE_PROFILE);
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
handleSecurityError(error);
|
||||
return;
|
||||
}
|
||||
const upload = await uploadProfileAvatarApi(prepared.path);
|
||||
if (!upload.ok) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ icon: 'none', title: upload.message || '上传失败' });
|
||||
return;
|
||||
}
|
||||
// saveProfile 失败时内部已 toast 具体原因,这里必须中断,避免弹出误导的成功提示
|
||||
const saved = await this.saveProfile({ nick_name: this.profile.nick_name, avatar: upload.url });
|
||||
uni.hideLoading();
|
||||
if (saved) {
|
||||
uni.showToast({ icon: 'success', title: '头像已更新' });
|
||||
}
|
||||
} catch (e) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ icon: 'none', title: (e && e.msg) || '上传失败' });
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
/** 打开昵称编辑抽屉(回填当前昵称) */
|
||||
openNickDrawer() {
|
||||
this.nickForm.nick_name = this.profile.nick_name || '';
|
||||
this.nickDrawerVisible = true;
|
||||
},
|
||||
/** 保存昵称(非空 + 长度校验,后端还有全表唯一校验) */
|
||||
async saveNickName() {
|
||||
const nickName = (this.nickForm.nick_name || '').trim();
|
||||
if (!nickName) {
|
||||
uni.showToast({ icon: 'none', title: '请输入昵称' });
|
||||
return;
|
||||
}
|
||||
if (nickName.length > 20) {
|
||||
uni.showToast({ icon: 'none', title: '昵称最长 20 个字' });
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
try {
|
||||
const ok = await this.saveProfile({ nick_name: nickName, avatar: this.profile.avatar || '' });
|
||||
if (ok) {
|
||||
this.nickDrawerVisible = false;
|
||||
uni.showToast({ icon: 'success', title: '保存成功' });
|
||||
}
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 提交资料保存(昵称/头像共用):成功后用返回的最新资料刷新页面并写回各端缓存
|
||||
* @returns {Promise<boolean>} 是否保存成功
|
||||
*/
|
||||
async saveProfile(payload) {
|
||||
try {
|
||||
const { data, ok, res } = await this.ctx.updateMyProfile(payload);
|
||||
if (!ok) {
|
||||
uni.showToast({ icon: 'none', title: (res && res.message) || '保存失败' });
|
||||
return false;
|
||||
}
|
||||
if (data) {
|
||||
this.profile = data;
|
||||
}
|
||||
applyProfileToCache(this.ctx, this.profile);
|
||||
return true;
|
||||
} catch (e) {
|
||||
// 网络异常等 reject:拦截器已统一 toast,这里只保证流程返回失败
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/** 打开修改密码抽屉(每次清空表单,避免残留上次输入) */
|
||||
openPwdDrawer() {
|
||||
this.pwdForm = { password: '', new_password: '', new_password_confirmation: '' };
|
||||
this.pwdDrawerVisible = true;
|
||||
},
|
||||
/** 提交改密:前端做基础校验,旧密码是否正确由后端校验 */
|
||||
async savePassword() {
|
||||
const { password, new_password, new_password_confirmation } = this.pwdForm;
|
||||
if (!password) {
|
||||
uni.showToast({ icon: 'none', title: '请输入旧密码' });
|
||||
return;
|
||||
}
|
||||
// 与 PC 端 passwordModalForm 同口径:5-18 位(后端 admin/update-password 共用)
|
||||
if (!new_password || new_password.length < 5 || new_password.length > 18) {
|
||||
uni.showToast({ icon: 'none', title: '新密码需 5-18 位' });
|
||||
return;
|
||||
}
|
||||
if (new_password !== new_password_confirmation) {
|
||||
uni.showToast({ icon: 'none', title: '两次密码不一致' });
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
try {
|
||||
const { ok, res } = await this.ctx.updateMyPassword({
|
||||
password,
|
||||
new_password,
|
||||
new_password_confirmation,
|
||||
});
|
||||
if (!ok) {
|
||||
uni.showToast({ icon: 'none', title: (res && res.message) || '修改失败' });
|
||||
return;
|
||||
}
|
||||
this.pwdDrawerVisible = false;
|
||||
uni.showToast({ icon: 'success', title: '修改成功' });
|
||||
} catch (e) {
|
||||
// 网络异常等 reject:拦截器已统一 toast,这里只复位提交态
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$theme-dark: #4db6a8;
|
||||
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f7f8;
|
||||
}
|
||||
.profile-body {
|
||||
padding: 24rpx 24rpx 60rpx;
|
||||
}
|
||||
.panel {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #f2f3f5;
|
||||
}
|
||||
.row-last {
|
||||
border-bottom: none;
|
||||
}
|
||||
.row-hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.row-label {
|
||||
font-size: 30rpx;
|
||||
color: #31353d;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.row-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
.value-text {
|
||||
font-size: 28rpx;
|
||||
color: #4e5969;
|
||||
margin-right: 8rpx;
|
||||
max-width: 420rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.value-readonly {
|
||||
color: #86909c;
|
||||
margin-right: 0;
|
||||
}
|
||||
.avatar-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 8rpx;
|
||||
background: #f2f3f5;
|
||||
}
|
||||
.avatar-circle {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||
color: #fff;
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
/* 抽屉表单 */
|
||||
.drawer-form {
|
||||
padding: 32rpx 32rpx 48rpx;
|
||||
}
|
||||
.pwd-field {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.pwd-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #31353d;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.form-input {
|
||||
height: 88rpx;
|
||||
background: #f7f8fa;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1d2129;
|
||||
}
|
||||
.input-placeholder {
|
||||
color: #c9cdd4;
|
||||
}
|
||||
</style>
|
||||
@@ -13,35 +13,30 @@
|
||||
</view>
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!list.length" class="empty">暂无用户</view>
|
||||
<view
|
||||
<!-- 会员列表卡:统一 xk-list-card 规范(头像 + 昵称/ID/手机分层 + 就诊人标签 + 箭头) -->
|
||||
<xk-list-card
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="card"
|
||||
:avatar="avatarSrc(item.avatarurl)"
|
||||
:title="item.nickname || '—'"
|
||||
:arrow="true"
|
||||
@click="goPatients(item)"
|
||||
>
|
||||
<view class="user-row">
|
||||
<image class="avatar" :src="avatarSrc(item.avatarurl)" mode="aspectFill" />
|
||||
<view class="text">
|
||||
<text class="name">{{ item.nickname || '—' }}</text>
|
||||
<text class="sub">
|
||||
ID:{{ item.id || '—' }}
|
||||
<template v-if="item.mobile"> · {{ item.mobile }}</template>
|
||||
</text>
|
||||
<template #body>
|
||||
<text class="card-sub">ID:{{ item.id || '—' }}<template v-if="item.mobile"> · {{ item.mobile }}</template></text>
|
||||
<!-- 就诊人标签化:一行多枚、自动换行 -->
|
||||
<view v-if="item.patients && item.patients.length" class="patients">
|
||||
<view
|
||||
v-for="p in item.patients"
|
||||
:key="p.up_id"
|
||||
class="patient-tag"
|
||||
>
|
||||
<text class="tag-text">{{ patientTagText(p) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
<!-- 就诊人标签化:一行多枚、自动换行 -->
|
||||
<view v-if="item.patients && item.patients.length" class="patients">
|
||||
<view
|
||||
v-for="p in item.patients"
|
||||
:key="p.up_id"
|
||||
class="patient-tag"
|
||||
>
|
||||
<text class="tag-text">{{ patientTagText(p) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="patients empty-p">暂无就诊人</view>
|
||||
</view>
|
||||
<view v-else class="patients empty-p">暂无就诊人</view>
|
||||
</template>
|
||||
</xk-list-card>
|
||||
</view>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
@@ -121,22 +116,15 @@ export default {
|
||||
background: #6acdbb; color: #fff; padding: 16rpx 28rpx;
|
||||
border-radius: 12rpx; font-size: 26rpx;
|
||||
}
|
||||
.card {
|
||||
background: #fff; border-radius: 16rpx; padding: 20rpx 20rpx 16rpx; margin-bottom: 14rpx;
|
||||
}
|
||||
.user-row { display: flex; align-items: center; gap: 14rpx; }
|
||||
.avatar { width: 56rpx; height: 56rpx; border-radius: 50%; background: #e8f5f2; flex-shrink: 0; }
|
||||
.text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4rpx; }
|
||||
.name { font-size: 28rpx; color: #111827; font-weight: 500; }
|
||||
.sub { font-size: 22rpx; color: #9ca3af; }
|
||||
.chevron { font-size: 32rpx; color: #d1d5db; line-height: 1; }
|
||||
/* 卡内副文案行(body 插槽自带样式) */
|
||||
.card-sub { display: block; margin-top: 4rpx; font-size: 22rpx; color: #9ca3af; }
|
||||
.patients {
|
||||
margin-top: 14rpx; padding-top: 14rpx; border-top: 1rpx solid #eef2f1;
|
||||
display: flex; flex-wrap: wrap; gap: 12rpx;
|
||||
}
|
||||
.patient-tag {
|
||||
padding: 6rpx 16rpx; border-radius: 8rpx;
|
||||
background: #f0faf7; border: 1rpx solid #6acdbb;
|
||||
padding: 6rpx 16rpx; border-radius: 20rpx;
|
||||
background: #f0f9f7; border: 1rpx solid #c2ece2;
|
||||
}
|
||||
.tag-text { font-size: 22rpx; color: #1a9b88; line-height: 1.4; }
|
||||
.empty-p { font-size: 22rpx; color: #d1d5db; }
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
/**
|
||||
* 诊所管理员端 tabbar 配置(clinic-page-layout 与 sub_salesperson_manage/PageLayout 共用)
|
||||
* 三 tab:工作台 / 功能 / 我的;图标统一用 uView 图标名(与平台/业务员/推广员三端一致,去掉旧 PNG 依赖)
|
||||
*/
|
||||
export const TAB_WORKBENCH = 0
|
||||
export const TAB_MY = 1
|
||||
const ROOT = '/subPackages/sub_clinic_salesperson';
|
||||
export const TAB_FEATURES = 1
|
||||
export const TAB_MY = 2
|
||||
|
||||
const ROOT = '/subPackages/sub_clinic_admin'
|
||||
|
||||
export const CLINIC_TAB_ROUTES = [
|
||||
'/subPackages/sub_clinic_admin/home/index',
|
||||
'/subPackages/sub_clinic_admin/my/index',
|
||||
`${ROOT}/home/index`,
|
||||
`${ROOT}/features/index`,
|
||||
`${ROOT}/my/index`,
|
||||
]
|
||||
|
||||
export function getClinicTabList() {
|
||||
@@ -14,6 +22,12 @@ export function getClinicTabList() {
|
||||
text: '工作台',
|
||||
pagePath: `${ROOT}/home/index`,
|
||||
},
|
||||
{
|
||||
iconPath: 'grid',
|
||||
selectedIconPath: 'grid-fill',
|
||||
text: '功能',
|
||||
pagePath: `${ROOT}/features/index`,
|
||||
},
|
||||
{
|
||||
iconPath: 'account',
|
||||
selectedIconPath: 'account-fill',
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
<view class="clinic-body" :style="bodyStyle">
|
||||
<slot />
|
||||
</view>
|
||||
<!-- 高度显式传 rpx(100 与 body 预留一致):默认 50px 固定像素在宽屏机型会与内部 rpx 定位的 icon/文字冲突挤压 -->
|
||||
<u-tabbar
|
||||
v-if="showTabbar"
|
||||
:value="tabIndex"
|
||||
:list="tabList"
|
||||
:height="100"
|
||||
active-color="#6ACDBB"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
<template>
|
||||
<!-- 会员卡片:点击进入会员详情(开通信息/开通记录) -->
|
||||
<!-- 会员卡片:黑金质感卡面,点击进入会员详情(开通信息/开通记录) -->
|
||||
<view class="vip-card" @click="onTap">
|
||||
<view class="vip-card-inner">
|
||||
<!-- 卡面装饰层:右上金色光晕 + 斜切高光,营造实体卡光泽 -->
|
||||
<view class="vip-glow"></view>
|
||||
<view class="vip-sheen"></view>
|
||||
<view class="vip-badge-combo">
|
||||
<image
|
||||
v-if="levelBadge"
|
||||
class="vip-level"
|
||||
:src="levelBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="vip-badge-ring">
|
||||
<image
|
||||
v-if="levelBadge"
|
||||
class="vip-level"
|
||||
:src="levelBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="!levelBadge" class="vip-badge-placeholder">
|
||||
<text class="vip-badge-placeholder-text">V</text>
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
v-if="durationBadge"
|
||||
class="vip-ribbon"
|
||||
:src="durationBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="!durationBadge && !levelBadge" class="vip-badge-placeholder">
|
||||
<text class="vip-badge-placeholder-text">V</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="vip-info">
|
||||
<text class="vip-title">{{ levelName }}</text>
|
||||
<text class="vip-sub">{{ durationLabel }}</text>
|
||||
<view class="vip-title-row">
|
||||
<text class="vip-title">{{ levelName }}</text>
|
||||
<text class="vip-duration-chip">{{ durationLabel }}</text>
|
||||
</view>
|
||||
<text class="vip-expire">{{ expireText }}</text>
|
||||
<text v-if="hasOpenInfo && openTypeText" class="vip-meta">开通方式:{{ openTypeText }}</text>
|
||||
<text v-if="hasOpenInfo && operatorText" class="vip-meta">开通人:{{ operatorText }}</text>
|
||||
<text class="vip-hint">点击查看会员详情</text>
|
||||
<view class="vip-hint-row">
|
||||
<text class="vip-hint">查看会员详情</text>
|
||||
<text class="vip-hint-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -110,87 +120,152 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 黑金会员卡:深色曜石底 + 香槟金文字 + 光泽装饰,参考实体贵宾卡质感 */
|
||||
.vip-card {
|
||||
margin: 0 24rpx 24rpx;
|
||||
}
|
||||
.vip-card-inner {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 36rpx 32rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #4a6741 55%, #6acdbb 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(44, 62, 80, 0.18);
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
/* 深色底叠加暗金光晕:比单一渐变更有层次 */
|
||||
background:
|
||||
radial-gradient(120% 160% at 100% 0%, rgba(212, 175, 55, 0.22) 0%, rgba(212, 175, 55, 0) 45%),
|
||||
linear-gradient(135deg, #23201a 0%, #12100c 55%, #1f1a12 100%);
|
||||
border: 1rpx solid rgba(220, 186, 110, 0.35);
|
||||
box-shadow:
|
||||
0 12rpx 32rpx rgba(0, 0, 0, 0.28),
|
||||
0 2rpx 8rpx rgba(212, 175, 55, 0.12);
|
||||
}
|
||||
/* 组合徽标容器:等级在下,丝带叠在中下部 */
|
||||
/* 右上角金色光晕:模拟灯光打在卡面上的高光 */
|
||||
.vip-glow {
|
||||
position: absolute;
|
||||
top: -90rpx;
|
||||
right: -60rpx;
|
||||
width: 260rpx;
|
||||
height: 260rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(240, 217, 168, 0.28) 0%, rgba(240, 217, 168, 0) 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
/* 斜切高光条:静态流光,增加金属光泽感 */
|
||||
.vip-sheen {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 30%;
|
||||
width: 120rpx;
|
||||
transform: skewX(-24deg);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.05) 50%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
/* 组合徽标容器:金环衬底,等级徽标居中,期限丝带叠在下缘 */
|
||||
.vip-badge-combo {
|
||||
position: relative;
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
width: 168rpx;
|
||||
height: 168rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vip-badge-ring {
|
||||
width: 148rpx;
|
||||
height: 148rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle, rgba(212, 175, 55, 0.16) 0%, rgba(212, 175, 55, 0.04) 100%);
|
||||
border: 1rpx solid rgba(220, 186, 110, 0.4);
|
||||
box-shadow: inset 0 0 20rpx rgba(212, 175, 55, 0.15);
|
||||
}
|
||||
.vip-level {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
}
|
||||
.vip-ribbon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 10rpx;
|
||||
bottom: 4rpx;
|
||||
transform: translateX(-50%);
|
||||
width: 200rpx;
|
||||
height: 72rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
.vip-badge-placeholder {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vip-badge-placeholder-text {
|
||||
color: #fff;
|
||||
font-size: 52rpx;
|
||||
color: #f0d9a8;
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 2rpx 8rpx rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
.vip-info {
|
||||
margin-left: 24rpx;
|
||||
position: relative;
|
||||
margin-left: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.vip-title {
|
||||
color: #ffffff;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8rpx;
|
||||
.vip-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.vip-sub {
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 8rpx;
|
||||
.vip-title {
|
||||
color: #f5e3bd;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2rpx;
|
||||
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
/* 期限徽章:描边金字小胶囊,替代原来的普通文本行 */
|
||||
.vip-duration-chip {
|
||||
margin-left: 14rpx;
|
||||
padding: 2rpx 14rpx;
|
||||
font-size: 20rpx;
|
||||
color: #e8cf9a;
|
||||
border: 1rpx solid rgba(232, 207, 154, 0.5);
|
||||
border-radius: 999rpx;
|
||||
background: rgba(212, 175, 55, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vip-expire {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
color: rgba(240, 224, 190, 0.78);
|
||||
font-size: 22rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.vip-meta {
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
color: rgba(240, 224, 190, 0.6);
|
||||
font-size: 22rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.vip-hint-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
.vip-hint {
|
||||
margin-top: 10rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
color: rgba(232, 207, 154, 0.72);
|
||||
font-size: 20rpx;
|
||||
}
|
||||
.vip-hint-arrow {
|
||||
margin-left: 4rpx;
|
||||
color: rgba(232, 207, 154, 0.72);
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
39
subPackages/sub_clinic_admin/features/index.vue
Normal file
39
subPackages/sub_clinic_admin/features/index.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<clinic-page-layout title="功能" :show-tabbar="true" :tab-index="1">
|
||||
<workbench-features ref="features" :api="featuresApi"></workbench-features>
|
||||
</clinic-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 诊所管理员端「功能」tab 薄壳页
|
||||
* 壳页只负责:套本端 layout(navbar/tabbar 配置)+ 注入本端角色接口,
|
||||
* 搜索/分组滑动/长按收藏/使用上报等全部逻辑在主包共享组件 workbench-features(四端复用)
|
||||
*/
|
||||
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
||||
import {
|
||||
getClinicAdminWorkbenchEntries,
|
||||
getClinicAdminEntryUserData,
|
||||
toggleClinicAdminFavoriteEntry,
|
||||
reportClinicAdminEntryUsage,
|
||||
} from '@/api/clinicAdmin.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout },
|
||||
data() {
|
||||
return {
|
||||
/** 注入共享组件的本端接口集合(统一返回 { res, data, ok }) */
|
||||
featuresApi: {
|
||||
getEntries: getClinicAdminWorkbenchEntries,
|
||||
getUserData: getClinicAdminEntryUserData,
|
||||
toggleFavorite: toggleClinicAdminFavoriteEntry,
|
||||
reportUsage: reportClinicAdminEntryUsage,
|
||||
},
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 从功能跳转的页面返回时刷新常用/最近使用(首屏加载由组件自身 mounted 完成,此时 ref 还不存在)
|
||||
if (this.$refs.features) this.$refs.features.refreshUserData()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -27,73 +27,75 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容区:快捷服务(多彩微拟物双列卡片) -->
|
||||
<view class="content-wrapper">
|
||||
<view v-for="section in sections" :key="section.key" class="section-panel">
|
||||
<view class="panel-header">
|
||||
<text class="panel-title">{{ section.title }}</text>
|
||||
<!-- 经营看板:hero 下沉浮卡,今日/本月经营数据一览(对齐医生端看板视觉) -->
|
||||
<view class="dashboard-wrapper">
|
||||
<view class="dashboard-card">
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">今日订单</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value">{{ dashboard.today_order_count }}</text>
|
||||
<text class="stat-unit">单</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-grid">
|
||||
<view
|
||||
v-for="item in section.items"
|
||||
:key="item.key"
|
||||
class="service-card"
|
||||
hover-class="service-card-hover"
|
||||
@click="go(item.path)"
|
||||
>
|
||||
<view class="service-icon-box" :style="{ background: item.theme.bg }">
|
||||
<u-icon :name="item.icon" size="38" :color="item.theme.color"></u-icon>
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<text class="service-name">{{ item.label }}</text>
|
||||
<text class="service-desc">{{ item.desc || '快捷进入' }}</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">今日营业额</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value highlight">{{ dashboard.today_order_amount }}</text>
|
||||
<text class="stat-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">本月订单</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value">{{ dashboard.month_order_count }}</text>
|
||||
<text class="stat-unit">单</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">本月营业额</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value highlight">{{ dashboard.month_order_amount }}</text>
|
||||
<text class="stat-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">待发货</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value" :class="{ warn: dashboard.wait_send_count > 0 }">{{ dashboard.wait_send_count }}</text>
|
||||
<text class="stat-unit">单</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">下单会员</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value">{{ dashboard.member_count }}</text>
|
||||
<text class="stat-unit">人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容区:个人功能区(最近使用 / 我的常用 / 更多功能,全部入口收敛到功能 tab) -->
|
||||
<view class="content-wrapper">
|
||||
<workbench-user-entries
|
||||
:recents="recentItems"
|
||||
:favorites="favItems"
|
||||
:card-long="cardLong"
|
||||
@item-click="onEntryTap"
|
||||
@more="goFeatures"
|
||||
></workbench-user-entries>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</clinic-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
||||
import { getWarehouseProductTypeOptions, getClinicAdminStoreQr } from '@/api/clinicAdmin.js'
|
||||
import { getClinicAdminStoreQr, getClinicAdminDashboardStats, getClinicAdminEntryUserData, reportClinicAdminEntryUsage } from '@/api/clinicAdmin.js'
|
||||
import { openQrCodePreview, buildClinicQrPayload } from '@/utils/qrCodePreview.js'
|
||||
|
||||
// 全局主题色池
|
||||
const GLOBAL_THEMES = [
|
||||
{ bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' }, // 橙金
|
||||
{ bg: 'linear-gradient(135deg, #EFF6FF, #DBEAFE)', color: '#3B82F6' }, // 科技蓝
|
||||
{ bg: 'linear-gradient(135deg, #FAF5FF, #F3E8FF)', color: '#A855F7' }, // 优雅紫
|
||||
{ bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' }, // 安全绿
|
||||
]
|
||||
|
||||
// 补充了 desc 字段以适配新的 UI
|
||||
const FINANCE_MENUS = [
|
||||
{ key: 'withdrawal', label: '提现管理', desc: '提现流水查询', icon: 'red-packet-fill', theme: GLOBAL_THEMES[0], path: '/subPackages/sub_clinic_admin/withdrawal/index' },
|
||||
{ key: 'reconciliation', label: '对账单', desc: '财务对账明细', icon: 'file-text-fill', theme: GLOBAL_THEMES[1], path: '/subPackages/sub_clinic_admin/reconciliation/index' },
|
||||
]
|
||||
|
||||
const BUSINESS_MENUS = [
|
||||
{ key: 'order', label: '商品订单', desc: '本诊所订单概览', icon: 'bag-fill', theme: GLOBAL_THEMES[3], path: '/subPackages/sub_clinic_admin/order/index' },
|
||||
{ key: 'user-patient', label: '会员管理', desc: '本诊所微信用户与就诊人', icon: 'account', theme: GLOBAL_THEMES[0], path: '/subPackages/sub_business_shared/user-patient/index' },
|
||||
]
|
||||
|
||||
const SALESPERSON_MENUS = [
|
||||
{ key: 'salesperson-manage', label: '推广员管理', desc: '添加与结算推广员', icon: 'account-fill', theme: GLOBAL_THEMES[2], path: '/subPackages/sub_salesperson_manage/index?mode=clinic' },
|
||||
]
|
||||
|
||||
const WAREHOUSE_FALLBACK = {
|
||||
key: 'warehouse',
|
||||
label: '仓库管理',
|
||||
desc: '库存管理中心',
|
||||
icon: 'car-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #F3F4F6, #E5E7EB)', color: '#6B7280' }, // 占位灰
|
||||
path: '/subPackages/sub_clinic_admin/warehouse/index',
|
||||
}
|
||||
import { normalizeEntry } from '@/utils/workbenchEntries.js'
|
||||
import { getCardStyle } from '@/utils/workbenchCardPref.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout },
|
||||
@@ -104,32 +106,101 @@ export default {
|
||||
storeQrData: null,
|
||||
/** 门店类型 0诊所 1药店 */
|
||||
storeType: 0,
|
||||
warehouseMenus: [WAREHOUSE_FALLBACK],
|
||||
/** 我的常用/最近使用原始入口(归一化后存储,行为装饰在 computed 注入保证角标响应式) */
|
||||
favRaw: [],
|
||||
recentRaw: [],
|
||||
/** 卡片形态偏好:true 长卡一行两个 / false 金刚区宫格(我的页可切换,存本地) */
|
||||
cardLong: false,
|
||||
/** 经营看板数据(接口返回前展示 0,避免布局跳动) */
|
||||
dashboard: {
|
||||
today_order_count: 0,
|
||||
today_order_amount: 0,
|
||||
month_order_count: 0,
|
||||
month_order_amount: 0,
|
||||
wait_send_count: 0,
|
||||
member_count: 0,
|
||||
pending_withdrawal_count: 0,
|
||||
pending_withdrawal_amount: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
storeKindLabel() {
|
||||
return Number(this.storeType) === 1 ? '药店' : '诊所'
|
||||
},
|
||||
sections() {
|
||||
return [
|
||||
{ key: 'finance', title: '财务管理', items: FINANCE_MENUS },
|
||||
{ key: 'business', title: '业务管理', items: BUSINESS_MENUS },
|
||||
{ key: 'salesperson', title: '门店推广员', items: SALESPERSON_MENUS },
|
||||
{ key: 'warehouse', title: '仓库管理', items: this.warehouseMenus },
|
||||
]
|
||||
/** 我的常用(角标等行为装饰在这里注入,保证看板数据返回后角标响应式更新) */
|
||||
favItems() {
|
||||
return this.favRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
/** 最近使用 */
|
||||
recentItems() {
|
||||
return this.recentRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
// 卡片形态偏好每次 onShow 重读:我的页切换后返回立即生效
|
||||
this.cardLong = getCardStyle() === 'long'
|
||||
this.loadDashboard()
|
||||
this.loadUserEntries()
|
||||
const user = uni.getStorageSync('clinic_admin_user') || {}
|
||||
this.storeName = user.store_name || (user.store && user.store.name) || user.nick_name || '门店管理'
|
||||
if (user.store && user.store.type != null) {
|
||||
this.storeType = Number(user.store.type) === 1 ? 1 : 0
|
||||
}
|
||||
this.loadStoreQr()
|
||||
this.loadWarehouseMenus()
|
||||
},
|
||||
methods: {
|
||||
/** 拉取我的常用 + 最近使用:失败静默留空(首页有引导条 + 更多功能卡兜底,功能不可达时去功能 tab) */
|
||||
async loadUserEntries() {
|
||||
try {
|
||||
const wrap = await getClinicAdminEntryUserData()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.favRaw = this.normalizeList(wrap.data.favorites)
|
||||
this.recentRaw = this.normalizeList(wrap.data.recents)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('常用/最近使用加载失败', e)
|
||||
}
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与功能页同一映射) */
|
||||
normalizeList(list) {
|
||||
if (!Array.isArray(list)) return []
|
||||
const items = []
|
||||
list.forEach((entry) => {
|
||||
const item = normalizeEntry(entry)
|
||||
if (item) items.push(item)
|
||||
})
|
||||
return items
|
||||
},
|
||||
/** 按 code 注入本端行为:提现管理带待审核角标 */
|
||||
decorateItem(item) {
|
||||
if (item.key === 'clinic_admin_withdrawal') {
|
||||
return { ...item, badge: this.dashboard.pending_withdrawal_count }
|
||||
}
|
||||
return item
|
||||
},
|
||||
/** 入口点击:静默上报使用(保持最近使用新鲜)→ 跳转 */
|
||||
onEntryTap(item) {
|
||||
reportClinicAdminEntryUsage(item.key).catch(() => {})
|
||||
if (item.path) {
|
||||
uni.navigateTo({ url: item.path })
|
||||
}
|
||||
},
|
||||
/** 更多功能/空态引导:切到功能 tab(tabbar 页统一走 reLaunch) */
|
||||
goFeatures() {
|
||||
uni.reLaunch({ url: '/subPackages/sub_clinic_admin/features/index' })
|
||||
},
|
||||
/** 拉取经营看板统计:失败静默保持 0,不阻塞首页其他模块 */
|
||||
async loadDashboard() {
|
||||
try {
|
||||
const wrap = await getClinicAdminDashboardStats()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.dashboard = { ...this.dashboard, ...wrap.data }
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('看板统计加载失败', e)
|
||||
}
|
||||
},
|
||||
async loadStoreQr() {
|
||||
this.storeQrCode = ''
|
||||
this.storeQrData = null
|
||||
@@ -152,35 +223,6 @@ export default {
|
||||
const { type, payload } = buildClinicQrPayload(this.storeQrData)
|
||||
openQrCodePreview({ type, payload })
|
||||
},
|
||||
loadWarehouseMenus() {
|
||||
getWarehouseProductTypeOptions().then(w => {
|
||||
const raw = w.data
|
||||
const options = Array.isArray(raw) ? raw : []
|
||||
const warehouseMenus = options.map((opt, index) => {
|
||||
const value = opt.value != null ? opt.value : opt.id
|
||||
const label = opt.label || opt.name || '仓库'
|
||||
const title = label.indexOf('仓库') >= 0 ? label : label
|
||||
|
||||
// 为动态仓库分配不同的主题色
|
||||
const theme = GLOBAL_THEMES[index % GLOBAL_THEMES.length]
|
||||
|
||||
return {
|
||||
key: 'warehouse-' + value,
|
||||
label: title,
|
||||
desc: `${title}库存管理`, // 增强描述
|
||||
icon: 'car-fill', // 统一使用车辆图标代表仓库物流
|
||||
theme: theme,
|
||||
path: '/subPackages/sub_clinic_admin/warehouse/index?type=' + encodeURIComponent(value) + '&label=' + encodeURIComponent(title),
|
||||
}
|
||||
})
|
||||
this.warehouseMenus = warehouseMenus.length ? warehouseMenus : [WAREHOUSE_FALLBACK]
|
||||
}).catch(() => {
|
||||
this.warehouseMenus = [WAREHOUSE_FALLBACK]
|
||||
})
|
||||
},
|
||||
go(path) {
|
||||
uni.navigateTo({ url: path })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -271,78 +313,66 @@ export default {
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
/* ================= 主体内容区:快捷服务 ================= */
|
||||
/* ================= 经营看板:hero 下沉浮白卡(统一工作台看板规格) ================= */
|
||||
.dashboard-wrapper {
|
||||
margin: -30rpx 30rpx 0;
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 20rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
row-gap: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.stat-value-group {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
/* 数值:DIN 数字字体统一看板视觉语言 */
|
||||
.stat-value {
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B;
|
||||
font-family: "DIN Alternate", sans-serif;
|
||||
}
|
||||
|
||||
.stat-value.highlight {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.stat-value.warn {
|
||||
color: #F97316;
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
/* ================= 主体内容区:个人功能区(分区/宫格样式在 workbench-user-entries 组件内) ================= */
|
||||
.content-wrapper {
|
||||
margin-top: 30rpx; /* 轻微向上偏移,与顶部区域产生连接感 */
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section-panel {
|
||||
margin: 0 30rpx 30rpx;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B; /* 深石板灰 */
|
||||
}
|
||||
|
||||
/* 宫格卡片布局:与平台端/推广员端一致的两列 */
|
||||
.service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 28rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.service-card-hover {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.service-icon-box {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
box-shadow: inset 0 2rpx 4rpx rgba(255, 255, 255, 0.5); /* 顶部微高光,增加拟物质感 */
|
||||
}
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8; /* 银灰色 */
|
||||
}
|
||||
</style>
|
||||
@@ -1,76 +1,53 @@
|
||||
<template>
|
||||
<clinic-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||
<view class="mine-container">
|
||||
<!-- 资料区:圆形头像 + 姓名 + 角色(对齐医生「我的」) -->
|
||||
<view class="profile-panel">
|
||||
<view class="avatar-circle">
|
||||
<text>{{ avatarText }}</text>
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ displayName }}</text>
|
||||
<view class="role-badge">
|
||||
<text class="role-text">诊所管理员</text>
|
||||
</view>
|
||||
<text v-if="storeName && storeName !== '-'" class="store-line">{{ storeName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 会员卡片:功能列表入口上方 -->
|
||||
<vip-member-card :vip="vipInfo" />
|
||||
<!-- 白色菜单卡:绑定微信 + 切换账号 -->
|
||||
<view class="setting-panel">
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
<!-- 独立绿色退出按钮 -->
|
||||
<view class="logout-wrap">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||
@click="logout"
|
||||
>退出登录</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<clinic-page-layout title="我的" :show-tabbar="true" :tab-index="2">
|
||||
<!-- 页面主体统一收进 admin-my-panel(资料区 + 个人中心入口 + 菜单卡 + 退出) -->
|
||||
<admin-my-panel
|
||||
ref="myPanel"
|
||||
:display-name="displayName"
|
||||
:avatar="avatar"
|
||||
role-text="诊所管理员"
|
||||
:store-name="storeName"
|
||||
fallback-char="管"
|
||||
>
|
||||
<!-- 会员卡片:资料区与功能列表之间(仅诊所管理员端有) -->
|
||||
<template #after-profile>
|
||||
<vip-member-card :vip="vipInfo" />
|
||||
</template>
|
||||
</admin-my-panel>
|
||||
</clinic-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 诊所/门店管理员「我的」:布局对齐医生(菜单卡 + 绿色退出)
|
||||
* 资料区与功能入口之间展示门店 VIP 会员卡片
|
||||
* 诊所/门店管理员「我的」:主体复用 admin-my-panel 共享组件
|
||||
* 本页只保留数据加载(本地缓存 + my-info 刷新 VIP),展示与退出逻辑都在组件内
|
||||
*/
|
||||
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
||||
import VipMemberCard from '@/subPackages/sub_clinic_admin/components/vip-member-card.vue'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||
import AdminMyPanel from '@/components/admin-my-panel/admin-my-panel.vue'
|
||||
import { getClinicAdminMyInfo } from '@/api/clinicAdmin.js'
|
||||
import { clearLoginStorage } from '@/utils/loginSession.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout, VipMemberCard, AccountSwitchEntry, WxBindEntry },
|
||||
components: { ClinicPageLayout, VipMemberCard, AdminMyPanel },
|
||||
data() {
|
||||
return {
|
||||
displayName: '',
|
||||
avatar: '',
|
||||
storeName: '',
|
||||
vipInfo: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
avatarText() {
|
||||
const n = this.displayName || '管'
|
||||
return n.slice(0, 1)
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
this.loadLocalUser()
|
||||
this.refreshVip()
|
||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
||||
if (this.$refs.myPanel) this.$refs.myPanel.refreshWxBind()
|
||||
},
|
||||
methods: {
|
||||
/** 先读本地缓存,保证首屏有基础信息 */
|
||||
/** 先读本地缓存,保证首屏有基础信息(个人中心改完昵称/头像返回时也走这里立即生效) */
|
||||
loadLocalUser() {
|
||||
const user = uni.getStorageSync('clinic_admin_user') || {}
|
||||
this.displayName = user.nick_name || user.name || user.account || '管理员'
|
||||
this.avatar = user.avatar || ''
|
||||
this.storeName = user.store_name || (user.store && user.store.name) || '-'
|
||||
if (user.vip) {
|
||||
this.vipInfo = user.vip
|
||||
@@ -88,6 +65,9 @@ export default {
|
||||
if (info.nick_name || info.name) {
|
||||
this.displayName = info.nick_name || info.name || this.displayName
|
||||
}
|
||||
if (info.avatar) {
|
||||
this.avatar = info.avatar
|
||||
}
|
||||
if (info.store_name) {
|
||||
this.storeName = info.store_name
|
||||
}
|
||||
@@ -101,91 +81,6 @@ export default {
|
||||
console.warn('刷新诊所管理员VIP失败', e)
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定退出登录?',
|
||||
confirmColor: '#6ACDBB',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
clearLoginStorage()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$theme-dark: #4db6a8;
|
||||
$color-page-bg: #F5F7F8;
|
||||
$color-text-title: #222B2A;
|
||||
$color-text-muted: #86909C;
|
||||
|
||||
.mine-container {
|
||||
padding: 0 0 60rpx;
|
||||
background-color: $color-page-bg;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.profile-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #ffffff;
|
||||
padding: 60rpx 40rpx 50rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.avatar-circle {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
||||
color: #ffffff;
|
||||
font-size: 52rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-info {
|
||||
margin-left: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.user-name {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: $color-text-title;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.role-badge {
|
||||
align-self: flex-start;
|
||||
background-color: rgba(106, 205, 187, 0.1);
|
||||
border: 1rpx solid rgba(106, 205, 187, 0.2);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.role-text {
|
||||
font-size: 22rpx;
|
||||
color: $theme-dark;
|
||||
font-weight: 600;
|
||||
}
|
||||
.store-line {
|
||||
margin-top: 12rpx;
|
||||
font-size: 26rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
.setting-panel {
|
||||
background-color: #ffffff;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 8rpx 40rpx 16rpx;
|
||||
}
|
||||
.logout-wrap {
|
||||
padding: 0 40rpx 32rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
<template>
|
||||
<clinic-page-layout title="会员详情" :show-back="true">
|
||||
<view class="vip-detail-page">
|
||||
<!-- 当前会员信息卡 -->
|
||||
<!-- 当前会员信息卡:黑金质感,与「我的」页会员卡呼应 -->
|
||||
<view class="vip-summary">
|
||||
<view class="vip-glow"></view>
|
||||
<view class="vip-sheen"></view>
|
||||
<view class="vip-badge-combo">
|
||||
<image
|
||||
v-if="levelBadge"
|
||||
class="vip-level"
|
||||
:src="levelBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="vip-badge-ring">
|
||||
<image
|
||||
v-if="levelBadge"
|
||||
class="vip-level"
|
||||
:src="levelBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="!levelBadge" class="vip-badge-placeholder">
|
||||
<text class="vip-badge-placeholder-text">V</text>
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
v-if="durationBadge"
|
||||
class="vip-ribbon"
|
||||
:src="durationBadge"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="!durationBadge && !levelBadge" class="vip-badge-placeholder">
|
||||
<text class="vip-badge-placeholder-text">V</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="vip-summary-info">
|
||||
<text class="vip-summary-title">{{ levelText }}</text>
|
||||
<text class="vip-summary-sub">{{ durationLabel }}</text>
|
||||
<view class="vip-summary-title-row">
|
||||
<text class="vip-summary-title">{{ levelText }}</text>
|
||||
<text class="vip-summary-chip">{{ durationLabel }}</text>
|
||||
</view>
|
||||
<text class="vip-summary-expire">{{ expireText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -297,71 +303,125 @@ $color-text-muted: #86909c;
|
||||
background: $color-page-bg;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 黑金头卡:与「我的」页 vip-member-card 同一套视觉语言 */
|
||||
.vip-summary {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 36rpx 32rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #4a6741 55%, #6acdbb 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(44, 62, 80, 0.18);
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
background:
|
||||
radial-gradient(120% 160% at 100% 0%, rgba(212, 175, 55, 0.22) 0%, rgba(212, 175, 55, 0) 45%),
|
||||
linear-gradient(135deg, #23201a 0%, #12100c 55%, #1f1a12 100%);
|
||||
border: 1rpx solid rgba(220, 186, 110, 0.35);
|
||||
box-shadow:
|
||||
0 12rpx 32rpx rgba(0, 0, 0, 0.28),
|
||||
0 2rpx 8rpx rgba(212, 175, 55, 0.12);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.vip-glow {
|
||||
position: absolute;
|
||||
top: -90rpx;
|
||||
right: -60rpx;
|
||||
width: 260rpx;
|
||||
height: 260rpx;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(240, 217, 168, 0.28) 0%, rgba(240, 217, 168, 0) 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.vip-sheen {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 30%;
|
||||
width: 120rpx;
|
||||
transform: skewX(-24deg);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.05) 50%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
.vip-badge-combo {
|
||||
position: relative;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
width: 148rpx;
|
||||
height: 148rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vip-badge-ring {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle, rgba(212, 175, 55, 0.16) 0%, rgba(212, 175, 55, 0.04) 100%);
|
||||
border: 1rpx solid rgba(220, 186, 110, 0.4);
|
||||
box-shadow: inset 0 0 20rpx rgba(212, 175, 55, 0.15);
|
||||
}
|
||||
.vip-level {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 110rpx;
|
||||
height: 110rpx;
|
||||
}
|
||||
.vip-ribbon {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 8rpx;
|
||||
bottom: 2rpx;
|
||||
transform: translateX(-50%);
|
||||
width: 170rpx;
|
||||
height: 60rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
.vip-badge-placeholder {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.vip-badge-placeholder-text {
|
||||
color: #fff;
|
||||
color: #f0d9a8;
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 2rpx 8rpx rgba(212, 175, 55, 0.4);
|
||||
}
|
||||
.vip-summary-info {
|
||||
margin-left: 24rpx;
|
||||
position: relative;
|
||||
margin-left: 28rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.vip-summary-title {
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8rpx;
|
||||
.vip-summary-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.vip-summary-title {
|
||||
color: #f5e3bd;
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2rpx;
|
||||
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
.vip-summary-chip {
|
||||
margin-left: 14rpx;
|
||||
padding: 2rpx 14rpx;
|
||||
font-size: 20rpx;
|
||||
color: #e8cf9a;
|
||||
border: 1rpx solid rgba(232, 207, 154, 0.5);
|
||||
border-radius: 999rpx;
|
||||
background: rgba(212, 175, 55, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vip-summary-sub,
|
||||
.vip-summary-expire {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
color: rgba(240, 224, 190, 0.78);
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.info-panel {
|
||||
background: #fff;
|
||||
|
||||
@@ -88,9 +88,13 @@ $color-text-body: #4E5969;
|
||||
$color-text-muted: #86909C;
|
||||
$color-border: #F2F3F5;
|
||||
|
||||
/* iOS 卡片规格:与订单详情各面板一致(大圆角 + 发丝线 + 轻阴影) */
|
||||
.express-panel {
|
||||
background: #ffffff;
|
||||
padding: 32rpx 40rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,24 @@
|
||||
<view>
|
||||
<clinic-page-layout title="订单详情" :show-back="true">
|
||||
<view class="detail-container">
|
||||
<!-- 头部面板:通栏,展示金额与状态 -->
|
||||
<!-- 状态头卡:状态大字(状态色区分)+ 实付金额 + 可复制单号 -->
|
||||
<view v-if="info.id" class="detail-panel header-panel">
|
||||
<view class="order-head">
|
||||
<view class="status-head">
|
||||
<view class="status-main">
|
||||
<view class="status-dot" :style="{ background: statusColor }"></view>
|
||||
<text class="status-text" :style="{ color: statusColor }">{{ detailExtra.statusText }}</text>
|
||||
</view>
|
||||
<view class="pay-group">
|
||||
<text class="pay-label">实付</text>
|
||||
<text class="pay-total">{{ money(info.total_pay_price) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="order-no-row">
|
||||
<text class="order-no-main">订单号:{{ info.order_no }}</text>
|
||||
<text class="pay-total">{{ money(info.total_pay_price) }}</text>
|
||||
<view class="copy-btn" hover-class="copy-btn-hover" @tap="copyOrderNo"><text>复制</text></view>
|
||||
</view>
|
||||
|
||||
<view class="tags">
|
||||
<text class="modern-tag tag-status">{{ detailExtra.statusText }}</text>
|
||||
<text class="modern-tag tag-type">{{ detailExtra.prescriptionTypeText }}</text>
|
||||
<text class="modern-tag tag-delivery">{{ detailExtra.deliveryText }}</text>
|
||||
<text v-if="detailExtra.isProxyPay" class="modern-tag tag-proxy">代支付</text>
|
||||
@@ -285,12 +294,30 @@ export default {
|
||||
},
|
||||
canRefund() {
|
||||
return this.info.status === 2 || this.info.status === 3
|
||||
}
|
||||
},
|
||||
/** 订单状态色:待办橙 / 在途蓝 / 完成绿 / 退款红 / 取消灰(与列表状态语义一致) */
|
||||
statusColor() {
|
||||
const m = {
|
||||
0: '#FF7D00', 1: '#FF7D00', 2: '#3B82F6', 3: '#00B42A', 4: '#F53F3F',
|
||||
5: '#FF7D00', 6: '#00B42A', 7: '#00B42A', 8: '#F53F3F', 9: '#86909C',
|
||||
}
|
||||
return m[Number(this.info.status)] || '#86909C'
|
||||
},
|
||||
},
|
||||
onLoad(q) {
|
||||
this.id = Number(q.id || 0)
|
||||
this.load()
|
||||
},
|
||||
/** 下拉刷新:重拉详情(含物流),完成后收起刷新动画 */
|
||||
onPullDownRefresh() {
|
||||
const done = () => uni.stopPullDownRefresh()
|
||||
const p = this.load()
|
||||
if (p && p.then) {
|
||||
p.then(done, done)
|
||||
} else {
|
||||
done()
|
||||
}
|
||||
},
|
||||
/** 离开页关闭处方抽屉,避免 page-container 残留遮罩导致列表白屏 */
|
||||
onHide() {
|
||||
this.closePrescriptionPopup()
|
||||
@@ -321,12 +348,13 @@ export default {
|
||||
this.patientDetailName = this.info.patient || ''
|
||||
this.patientDetailVisible = true
|
||||
},
|
||||
/** 拉取订单详情;返回 Promise 供下拉刷新在完成后收起动画 */
|
||||
load() {
|
||||
if (!this.id) {
|
||||
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
getOrderDetail(this.id).then(w => {
|
||||
return getOrderDetail(this.id).then(w => {
|
||||
if (!w.ok) {
|
||||
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||
return
|
||||
@@ -341,6 +369,13 @@ export default {
|
||||
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
/** 复制订单号(状态头卡复制按钮) */
|
||||
copyOrderNo() {
|
||||
uni.setClipboardData({
|
||||
data: String(this.info.order_no || ''),
|
||||
success: () => uni.showToast({ title: '订单号已复制', icon: 'none' }),
|
||||
})
|
||||
},
|
||||
loadExpress() {
|
||||
getExpressDetailByOrderId(this.id).then(w => {
|
||||
if (w.ok) this.expressInfo = w.data
|
||||
@@ -395,48 +430,110 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$color-page-bg: #F5F7F8;
|
||||
$color-text-title: #222B2A;
|
||||
$color-page-bg: #F4F6F8;
|
||||
$color-text-title: #1C1C1E;
|
||||
$color-text-body: #4E5969;
|
||||
$color-text-muted: #86909C;
|
||||
$color-text-muted: #8E8E93;
|
||||
$color-border: #F2F3F5;
|
||||
$color-danger: #F53F3F;
|
||||
|
||||
.detail-container {
|
||||
background-color: $color-page-bg;
|
||||
min-height: 100vh;
|
||||
/* 适配底部操作栏,留出极大的安全边距 */
|
||||
padding-bottom: 180rpx;
|
||||
/* iOS 卡片流:两侧留白 + 适配底部操作栏的安全边距 */
|
||||
padding: 20rpx 24rpx 180rpx;
|
||||
}
|
||||
|
||||
/* 通栏面板 */
|
||||
/* iOS 卡片面板:大圆角 + 发丝线 + 轻阴影(与工作台卡片同规格) */
|
||||
.detail-panel {
|
||||
background: #ffffff;
|
||||
padding: 32rpx 40rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.header-panel {
|
||||
padding-top: 40rpx;
|
||||
padding-top: 32rpx;
|
||||
}
|
||||
|
||||
.order-head {
|
||||
/* 状态头:左状态大字(色点 + 文案),右实付金额 */
|
||||
.status-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-no-main {
|
||||
font-size: 28rpx;
|
||||
.status-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pay-group {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.pay-label {
|
||||
font-size: 22rpx;
|
||||
color: $color-text-muted;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
/* 单号行:灰底圆角条 + 复制按钮 */
|
||||
.order-no-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: rgba(60, 60, 67, 0.04);
|
||||
border-radius: 16rpx;
|
||||
padding: 14rpx 20rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.order-no-main {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-muted;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
padding: 4rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid $theme-primary;
|
||||
text {
|
||||
font-size: 22rpx;
|
||||
color: $theme-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-btn-hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pay-total {
|
||||
font-size: 40rpx;
|
||||
color: $color-text-title;
|
||||
font-weight: 700;
|
||||
font-family: monospace, sans-serif;
|
||||
font-family: "DIN Alternate", monospace, sans-serif;
|
||||
}
|
||||
|
||||
.tags {
|
||||
@@ -453,7 +550,6 @@ $color-danger: #F53F3F;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag-status { background: rgba(106, 205, 187, 0.1); color: $theme-primary; }
|
||||
.tag-type { background: rgba(230, 162, 60, 0.1); color: #e6a23c; }
|
||||
.tag-delivery { background: rgba(91, 127, 214, 0.1); color: #5b7fd6; }
|
||||
.tag-proxy { background: rgba(114, 46, 209, 0.1); color: #722ED1; }
|
||||
@@ -638,6 +734,7 @@ $color-danger: #F53F3F;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #ffffff;
|
||||
border-top: 1rpx solid rgba(60, 60, 67, 0.08);
|
||||
padding: 24rpx 40rpx;
|
||||
/* 自动撑起 iOS 底部安全距离 */
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
|
||||
@@ -9,6 +9,12 @@ export function getClinicSalespersonContext() {
|
||||
text: '工作台',
|
||||
pagePath: `${ROOT}/home/index`,
|
||||
},
|
||||
{
|
||||
iconPath: 'grid',
|
||||
selectedIconPath: 'grid-fill',
|
||||
text: '功能',
|
||||
pagePath: `${ROOT}/features/index`,
|
||||
},
|
||||
{
|
||||
iconPath: 'account',
|
||||
selectedIconPath: 'account-fill',
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
<view class="clinic-body" :style="bodyStyle">
|
||||
<slot />
|
||||
</view>
|
||||
<!-- 高度显式传 rpx(100 与 body 预留一致):默认 50px 固定像素在宽屏机型会与内部 rpx 定位的 icon/文字冲突挤压 -->
|
||||
<u-tabbar
|
||||
v-if="showTabbar"
|
||||
:value="tabIndex"
|
||||
:list="tabList"
|
||||
:height="100"
|
||||
active-color="#6ACDBB"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout title="我的收益" :show-back="true">
|
||||
<view class="page">
|
||||
<view class="card" v-for="item in cards" :key="item.label">
|
||||
<text class="label">{{ item.label }}</text>
|
||||
<text class="value">{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
data() {
|
||||
return { cards: [] }
|
||||
},
|
||||
onLoad() {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
const wrap = await getClinicSalespersonMyInfo()
|
||||
if (!wrap || !wrap.ok) return
|
||||
const stats = wrap.data?.stats || {}
|
||||
this.cards = [
|
||||
{ label: '累计服务费', value: `¥${stats.money || '0.00'}` },
|
||||
{ label: '待结算', value: `¥${stats.pending_amount || '0.00'}` },
|
||||
{ label: '已结算', value: `¥${stats.settled_amount || '0.00'}` },
|
||||
{ label: '累计获客', value: `${stats.user_number || 0} 人` },
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page { padding: 24rpx; }
|
||||
.card { background: #fff; border-radius: 16rpx; padding: 32rpx; margin-bottom: 20rpx; display: flex; justify-content: space-between; }
|
||||
.label { color: #666; font-size: 28rpx; }
|
||||
.value { font-size: 32rpx; font-weight: 600; color: #333; }
|
||||
</style>
|
||||
50
subPackages/sub_clinic_salesperson/features/index.vue
Normal file
50
subPackages/sub_clinic_salesperson/features/index.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout title="功能" :show-tabbar="true" :tab-index="1">
|
||||
<workbench-features ref="features" :api="featuresApi" @special="onSpecialEntry"></workbench-features>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 诊所推广员端「功能」tab 薄壳页
|
||||
* 壳页只负责:套本端 layout(navbar/tabbar 配置)+ 注入本端角色接口 + 处理本端特殊入口,
|
||||
* 搜索/分组滑动/长按收藏/使用上报等全部逻辑在主包共享组件 workbench-features(四端复用)
|
||||
*/
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import {
|
||||
getClinicSalespersonWorkbenchEntries,
|
||||
getClinicSalespersonEntryUserData,
|
||||
toggleClinicSalespersonFavoriteEntry,
|
||||
reportClinicSalespersonEntryUsage,
|
||||
} from '@/api/clinicSalesperson.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout },
|
||||
data() {
|
||||
return {
|
||||
/** 注入共享组件的本端接口集合(统一返回 { res, data, ok }) */
|
||||
featuresApi: {
|
||||
getEntries: getClinicSalespersonWorkbenchEntries,
|
||||
getUserData: getClinicSalespersonEntryUserData,
|
||||
toggleFavorite: toggleClinicSalespersonFavoriteEntry,
|
||||
reportUsage: reportClinicSalespersonEntryUsage,
|
||||
},
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 从功能跳转的页面返回时刷新常用/最近使用(首屏加载由组件自身 mounted 完成,此时 ref 还不存在)
|
||||
if (this.$refs.features) this.$refs.features.refreshUserData()
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 无 path 的特殊入口处理:cs_transfer 的传方方式弹窗与拍方抽屉都挂在首页,
|
||||
* 带 openTransfer 参数回首页自动弹出,避免在功能页重复引入整套传方组件
|
||||
*/
|
||||
onSpecialEntry(item) {
|
||||
if (item.key === 'cs_transfer') {
|
||||
uni.reLaunch({ url: '/subPackages/sub_clinic_salesperson/home/index?openTransfer=1' })
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -76,31 +76,14 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷服务区:多彩微拟物质感 -->
|
||||
<view class="section-panel">
|
||||
<view class="panel-header">
|
||||
<text class="panel-title">常用服务</text>
|
||||
</view>
|
||||
|
||||
<view class="service-grid">
|
||||
<view
|
||||
v-for="(item, index) in enrichedMenuItems"
|
||||
:key="item.key"
|
||||
class="service-card"
|
||||
hover-class="service-card-hover"
|
||||
@click="onMenuClick(item)"
|
||||
>
|
||||
<view class="service-icon-box" :style="{ background: item.theme.bg }">
|
||||
<!-- 替换为 uView 动态图标 -->
|
||||
<u-icon :name="item.icon" size="38" :color="item.theme.color"></u-icon>
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<text class="service-name">{{ item.label }}</text>
|
||||
<text class="service-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 主体内容区:个人功能区(最近使用 / 我的常用 / 更多功能,全部入口收敛到功能 tab) -->
|
||||
<workbench-user-entries
|
||||
:recents="recentItems"
|
||||
:favorites="favItems"
|
||||
:card-long="cardLong"
|
||||
@item-click="onEntryTap"
|
||||
@more="goFeatures"
|
||||
></workbench-user-entries>
|
||||
|
||||
</view>
|
||||
<transfer-mode-modal v-model="showTransferModeModal" @select="onTransferModeSelect" />
|
||||
@@ -118,10 +101,10 @@
|
||||
<script>
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import TransferModeModal from '@/subPackages/sub_clinic_salesperson/components/TransferModeModal.vue'
|
||||
import { getClinicSalespersonMyInfo, createClinicSalespersonTransferPrescription } from '@/api/clinicSalesperson.js'
|
||||
import { getClinicSalespersonMyInfo, createClinicSalespersonTransferPrescription, getClinicSalespersonEntryUserData, reportClinicSalespersonEntryUsage } from '@/api/clinicSalesperson.js'
|
||||
import { openQrCodePreview, buildSalespersonQrPayload } from '@/utils/qrCodePreview.js'
|
||||
|
||||
const ROOT = '/subPackages/sub_clinic_salesperson'
|
||||
import { normalizeEntry } from '@/utils/workbenchEntries.js'
|
||||
import { getCardStyle } from '@/utils/workbenchCardPref.js'
|
||||
|
||||
export default {
|
||||
// transfer-patient-drawer 跨分包引用,在 pages.json 通过 usingComponents + componentPlaceholder 注册
|
||||
@@ -136,44 +119,74 @@ export default {
|
||||
showTransferModeModal: false,
|
||||
showPhotoTransferDrawer: false,
|
||||
photoSubmitting: false,
|
||||
menuItems: [
|
||||
{ key: 'transfer', label: '传方', action: 'transfer' },
|
||||
{ key: 'transfer-list', label: '传方记录', path: `${ROOT}/transfer-prescription/list` },
|
||||
// { key: 'earnings', label: '我的收益', path: `${ROOT}/earnings/index` },
|
||||
{ key: 'leads', label: '获客记录', path: `${ROOT}/leads/index` },
|
||||
{ key: 'invitees', label: '我邀请的推广员', path: `${ROOT}/invitees/index` },
|
||||
{ key: 'commission', label: '服务费记录', path: `${ROOT}/commission/index` },
|
||||
{ key: 'settlement', label: '结算记录', path: `${ROOT}/settlement/index` },
|
||||
],
|
||||
/** 我的常用/最近使用原始入口(归一化后存储,传方弹窗行为在 computed 注入) */
|
||||
favRaw: [],
|
||||
recentRaw: [],
|
||||
/** 卡片形态偏好:true 长卡一行两个 / false 金刚区宫格(我的页可切换,存本地) */
|
||||
cardLong: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 动态注入多彩主题和增强文案,使用 uView 图标名称
|
||||
enrichedMenuItems() {
|
||||
const themes = [
|
||||
{ bg: 'linear-gradient(135deg, #ECFDF5, #D1FAE5)', color: '#10B981', desc: '线下中药传方', icon: 'edit-pen-fill' },
|
||||
{ bg: 'linear-gradient(135deg, #F0F9FF, #E0F2FE)', color: '#0EA5E9', desc: '历史传方记录', icon: 'file-text-fill' },
|
||||
// 收益:使用钻石/钱包相关图标
|
||||
{ bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316', desc: '查看收益明细', icon: 'red-packet-fill' },
|
||||
// 获客:使用目标/数据相关图标
|
||||
{ bg: 'linear-gradient(135deg, #EFF6FF, #DBEAFE)', color: '#3B82F6', desc: '客户转化漏斗', icon: 'bag-fill' },
|
||||
// 分成:使用图表/比例相关图标
|
||||
{ bg: 'linear-gradient(135deg, #FAF5FF, #F3E8FF)', color: '#A855F7', desc: '服务费与记录', icon: 'red-packet-fill' },
|
||||
// 结算:使用卡片/提现相关图标
|
||||
{ bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E', desc: '提现与账单', icon: 'rmb-circle-fill' },
|
||||
];
|
||||
return this.menuItems.map((item, index) => ({
|
||||
...item,
|
||||
icon: themes[index % themes.length].icon,
|
||||
desc: themes[index % themes.length].desc,
|
||||
theme: themes[index % themes.length]
|
||||
}));
|
||||
/** 我的常用(cs_transfer 无 path,按 code 注入传方弹窗行为) */
|
||||
favItems() {
|
||||
return this.favRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
/** 最近使用 */
|
||||
recentItems() {
|
||||
return this.recentRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
},
|
||||
onLoad(options) {
|
||||
// 功能页点击 cs_transfer(传方弹窗挂在本页)时带 openTransfer=1 reLaunch 回来自动弹出
|
||||
if (options && options.openTransfer) {
|
||||
this.showTransferModeModal = true
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 卡片形态偏好每次 onShow 重读:我的页切换后返回立即生效
|
||||
this.cardLong = getCardStyle() === 'long'
|
||||
this.loadInfo()
|
||||
this.loadUserEntries()
|
||||
},
|
||||
methods: {
|
||||
/** 拉取我的常用 + 最近使用:失败静默留空(首页有引导条 + 更多功能卡兜底) */
|
||||
async loadUserEntries() {
|
||||
try {
|
||||
const wrap = await getClinicSalespersonEntryUserData()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.favRaw = this.normalizeList(wrap.data.favorites)
|
||||
this.recentRaw = this.normalizeList(wrap.data.recents)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('常用/最近使用加载失败', e)
|
||||
}
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与功能页同一映射) */
|
||||
normalizeList(list) {
|
||||
if (!Array.isArray(list)) return []
|
||||
const items = []
|
||||
list.forEach((entry) => {
|
||||
const item = normalizeEntry(entry)
|
||||
if (item) items.push(item)
|
||||
})
|
||||
return items
|
||||
},
|
||||
/** 按 code 注入本端行为:传方入口弹方式选择弹窗(无 path) */
|
||||
decorateItem(item) {
|
||||
if (item.key === 'cs_transfer') {
|
||||
return { ...item, action: 'transfer' }
|
||||
}
|
||||
return item
|
||||
},
|
||||
/** 入口点击:静默上报使用(保持最近使用新鲜)→ 传方弹窗/普通跳转 */
|
||||
onEntryTap(item) {
|
||||
reportClinicSalespersonEntryUsage(item.key).catch(() => {})
|
||||
this.onMenuClick(item)
|
||||
},
|
||||
/** 更多功能/空态引导:切到功能 tab(tabbar 页统一走 reLaunch) */
|
||||
goFeatures() {
|
||||
uni.reLaunch({ url: '/subPackages/sub_clinic_salesperson/features/index' })
|
||||
},
|
||||
async loadInfo() {
|
||||
const wrap = await getClinicSalespersonMyInfo()
|
||||
if (!wrap || !wrap.ok) return
|
||||
@@ -520,72 +533,5 @@ export default {
|
||||
}
|
||||
|
||||
/* ================= 快捷服务区:多彩拟物 ================= */
|
||||
.section-panel {
|
||||
margin: 0 30rpx;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 28rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.service-card-hover {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.service-icon-box {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
box-shadow: inset 0 2rpx 4rpx rgba(255, 255, 255, 0.5); /* 顶部微高光,增加拟物质感 */
|
||||
}
|
||||
|
||||
/* 移除了无用的 .service-icon 类 */
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
/* 个人功能区(分区/宫格样式在 workbench-user-entries 组件内) */
|
||||
</style>
|
||||
@@ -2,13 +2,15 @@
|
||||
<clinic-salesperson-page-layout title="获客记录" :show-back="true">
|
||||
<view class="page">
|
||||
<view v-if="!list.length" class="empty">暂无获客记录</view>
|
||||
<view v-for="item in list" :key="item.id" class="row">
|
||||
<view>
|
||||
<text class="name">{{ item.user_nick_name || '用户' }}</text>
|
||||
<text class="phone">{{ item.user_phone }}</text>
|
||||
</view>
|
||||
<text class="time">{{ item.bind_at_text }}</text>
|
||||
</view>
|
||||
<!-- 获客记录卡:统一 xk-list-card 规范(首字头像 + 昵称/手机分层 + 绑定时间) -->
|
||||
<xk-list-card
|
||||
v-for="item in list" :key="item.id"
|
||||
:avatar-text="item.user_nick_name || '用'"
|
||||
:title="item.user_nick_name || '用户'"
|
||||
:time="item.bind_at_text"
|
||||
:rows="[item.user_phone || '-']"
|
||||
:clickable="false"
|
||||
></xk-list-card>
|
||||
</view>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
@@ -37,8 +39,4 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.page { padding: 24rpx; }
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
.row { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { display: block; font-size: 28rpx; font-weight: 600; }
|
||||
.phone { font-size: 24rpx; color: #666; }
|
||||
.time { font-size: 22rpx; color: #999; }
|
||||
</style>
|
||||
|
||||
@@ -1,70 +1,55 @@
|
||||
<template>
|
||||
<clinic-salesperson-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||
<view class="mine-container">
|
||||
<view class="profile-panel">
|
||||
<view class="avatar-circle">
|
||||
<text>{{ avatarText }}</text>
|
||||
</view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ displayName }}</text>
|
||||
<view class="role-badge">
|
||||
<text class="role-text">推广员</text>
|
||||
</view>
|
||||
<text v-if="phone" class="meta-line">{{ phone }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="storeName" class="setting-panel info-panel">
|
||||
<view class="info-row">
|
||||
<text class="item-label">所属诊所</text>
|
||||
<text class="item-value">{{ storeName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-panel">
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
<view class="logout-wrap">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||
@click="logout"
|
||||
>退出登录</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 页面主体统一收进 admin-my-panel(资料区 + 个人中心入口 + 菜单卡 + 退出) -->
|
||||
<admin-my-panel
|
||||
ref="myPanel"
|
||||
:display-name="displayName"
|
||||
:avatar="avatar"
|
||||
role-text="推广员"
|
||||
:store-name="storeName"
|
||||
:phone="phone"
|
||||
fallback-char="推"
|
||||
/>
|
||||
</clinic-salesperson-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 推广员「我的」:对齐医生(菜单卡 + 绿色退出),保留所属诊所与绑定微信
|
||||
* 推广员「我的」:主体复用 admin-my-panel 共享组件
|
||||
* 本页只保留 my-info 拉取(姓名/手机号/所属诊所)并写回缓存
|
||||
*/
|
||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||
import AdminMyPanel from '@/components/admin-my-panel/admin-my-panel.vue'
|
||||
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
|
||||
import { clearLoginStorage } from '@/utils/loginSession.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicSalespersonPageLayout, AccountSwitchEntry, WxBindEntry },
|
||||
components: { ClinicSalespersonPageLayout, AdminMyPanel },
|
||||
data() {
|
||||
return {
|
||||
displayName: '',
|
||||
avatar: '',
|
||||
phone: '',
|
||||
storeName: '',
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
avatarText() {
|
||||
return (this.displayName || '推').slice(0, 1)
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
// 先读缓存(个人中心改完昵称/头像返回立即生效),再拉接口刷新
|
||||
this.loadLocalUser()
|
||||
this.loadProfile()
|
||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
||||
if (this.$refs.myPanel) this.$refs.myPanel.refreshWxBind()
|
||||
},
|
||||
methods: {
|
||||
/** 读本地缓存兜底首屏(my-info 缓存结构:{ salesperson, store }) */
|
||||
loadLocalUser() {
|
||||
const info = uni.getStorageSync('clinic_salesperson_user') || {}
|
||||
const sp = info.salesperson || {}
|
||||
this.displayName = sp.nick_name || this.displayName || '推广员'
|
||||
this.avatar = sp.avatar || ''
|
||||
this.phone = sp.phone || ''
|
||||
this.storeName = (info.store && info.store.name) || ''
|
||||
},
|
||||
/** 拉取 my-info 刷新资料并写回缓存 */
|
||||
async loadProfile() {
|
||||
if (this.loading) return
|
||||
this.loading = true
|
||||
@@ -72,117 +57,16 @@ export default {
|
||||
const wrap = await getClinicSalespersonMyInfo()
|
||||
if (!wrap || !wrap.ok) return
|
||||
const info = wrap.data || {}
|
||||
this.displayName = info.salesperson?.nick_name || '推广员'
|
||||
this.phone = info.salesperson?.phone || ''
|
||||
this.storeName = info.store?.name || ''
|
||||
const sp = info.salesperson || {}
|
||||
this.displayName = sp.nick_name || '推广员'
|
||||
this.avatar = sp.avatar || ''
|
||||
this.phone = sp.phone || ''
|
||||
this.storeName = (info.store && info.store.name) || ''
|
||||
uni.setStorageSync('clinic_salesperson_user', info)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定退出登录?',
|
||||
confirmColor: '#6ACDBB',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
clearLoginStorage()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$theme-primary: #6acdbb;
|
||||
$theme-dark: #4db6a8;
|
||||
$color-page-bg: #f5f7f8;
|
||||
$color-text-title: #222b2a;
|
||||
$color-text-muted: #86909c;
|
||||
|
||||
.mine-container {
|
||||
padding: 0 0 60rpx;
|
||||
background-color: $color-page-bg;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.profile-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
padding: 60rpx 40rpx 50rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
.avatar-circle {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
||||
color: #fff;
|
||||
font-size: 52rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.user-info {
|
||||
margin-left: 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.user-name {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: $color-text-title;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.role-badge {
|
||||
align-self: flex-start;
|
||||
background-color: rgba(106, 205, 187, 0.1);
|
||||
border: 1rpx solid rgba(106, 205, 187, 0.2);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.role-text {
|
||||
font-size: 22rpx;
|
||||
color: $theme-dark;
|
||||
font-weight: 600;
|
||||
}
|
||||
.meta-line {
|
||||
font-size: 26rpx;
|
||||
color: $color-text-muted;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
.setting-panel {
|
||||
background-color: #fff;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 8rpx 40rpx 16rpx;
|
||||
}
|
||||
.info-panel {
|
||||
padding: 28rpx 40rpx;
|
||||
}
|
||||
.info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.item-label {
|
||||
font-size: 30rpx;
|
||||
color: $color-text-title;
|
||||
font-weight: 500;
|
||||
}
|
||||
.item-value {
|
||||
font-size: 30rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
.logout-wrap {
|
||||
padding: 0 40rpx 32rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<view />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const TRANSFER_RX_URL = '/subPackages/sub_workbench/prescription_v2/index?salesperson_transfer=1&initial_category=1'
|
||||
|
||||
export default {
|
||||
onLoad() {
|
||||
uni.redirectTo({ url: TRANSFER_RX_URL })
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -2,24 +2,29 @@
|
||||
<clinic-salesperson-page-layout title="传方记录" :show-back="true">
|
||||
<view class="page">
|
||||
<view v-if="!list.length && !loading" class="empty">暂无传方记录</view>
|
||||
<view v-for="item in list" :key="item.id" class="row" @click="openDetail(item.id)">
|
||||
<view class="top">
|
||||
<text class="name">{{ item.patient_name }} {{ item.patient_mobile }}</text>
|
||||
<u-tag
|
||||
:text="item.status_text || (item.is_imported ? '已完成' : '未处理')"
|
||||
:type="statusTagType(item.status)"
|
||||
size="mini"
|
||||
/>
|
||||
</view>
|
||||
<text class="meta">{{ item.transfer_mode_text || '' }} · {{ item.transfer_time_text }}</text>
|
||||
<text v-if="item.drug_text_lines && item.drug_text_lines.length" class="drugs">
|
||||
药方:{{ item.drug_text_lines.join(';') }}
|
||||
</text>
|
||||
<text v-if="item.remark" class="remark">备注:{{ item.remark }}</text>
|
||||
<text v-if="item.status === 3 && item.exception_message" class="exception">
|
||||
异常:{{ item.exception_message }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 传方记录卡:统一 xk-list-card 规范(患者信息分层 + 状态标签 + 查看详情操作) -->
|
||||
<xk-list-card
|
||||
v-for="item in list" :key="item.id"
|
||||
:avatar-text="item.patient_name"
|
||||
:title="item.patient_name"
|
||||
:meta="item.patient_mobile"
|
||||
:time="item.transfer_time_text"
|
||||
:tags="[{ text: item.status_text || (item.is_imported ? '已完成' : '未处理'), type: statusTagType(item.status) }]"
|
||||
:actions="[{ key: 'detail', text: '查看详情', type: 'ghost' }]"
|
||||
@action="openDetail(item.id)"
|
||||
@click="openDetail(item.id)"
|
||||
>
|
||||
<template #body>
|
||||
<text class="card-sub" v-if="item.transfer_mode_text">{{ item.transfer_mode_text }}</text>
|
||||
<text v-if="item.drug_text_lines && item.drug_text_lines.length" class="card-sub drugs">
|
||||
药方:{{ item.drug_text_lines.join(';') }}
|
||||
</text>
|
||||
<text v-if="item.remark" class="card-sub">备注:{{ item.remark }}</text>
|
||||
<text v-if="item.status === 3 && item.exception_message" class="card-sub exception">
|
||||
异常:{{ item.exception_message }}
|
||||
</text>
|
||||
</template>
|
||||
</xk-list-card>
|
||||
<u-button v-if="list.length" plain type="primary" class="mt" @click="goForm">新建传方</u-button>
|
||||
</view>
|
||||
<transfer-detail-drawer ref="detailDrawer" />
|
||||
@@ -72,10 +77,14 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.page { padding: 24rpx; }
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
.row { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.top { display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { font-size: 28rpx; font-weight: 600; }
|
||||
.meta, .drugs, .remark { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
.exception { display: block; font-size: 24rpx; color: #ef4444; margin-top: 8rpx; }
|
||||
/* 卡内副文案行(body 插槽自带样式):药方允许两行截断,异常标红 */
|
||||
.card-sub { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; }
|
||||
.drugs {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
.exception { color: #ef4444; }
|
||||
.mt { margin-top: 24rpx; }
|
||||
</style>
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- <view class="search white">
|
||||
<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-col b-r-8 white p-32 m-t-2 p-t-0" >
|
||||
<view class="flex-row m-t-32 flex-ali-center" v-for="(item,index) in list" :key="index"
|
||||
@click="changeSelect(index)">
|
||||
<view style="width: 40rpx;height: 40rpx;" v-if="item.select">
|
||||
<u-icon name="checkmark-circle-fill" color="#6ACDBB" size="40"></u-icon>
|
||||
</view>
|
||||
<view class="select" v-else></view>
|
||||
<!-- <view class="m-l-32">
|
||||
<u-image width="80rpx" height="80rpx" :src="item.cover">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
</view> -->
|
||||
<view class="flex-col m-l-2 flex-ali-center">
|
||||
<view class="flex-row">
|
||||
<d-text :text="item.title" className="content-c fs-32"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="flex-row flex-ali-center" v-if="item.tags&&item.tags.length>0">
|
||||
<view class="m-r08" v-for="(it,idx) in item.tags" :key="idx">
|
||||
<u-tag :text="it" 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 class="button">
|
||||
<u-button :throttle-time="0" shape="circle" @click="submit" :ripple="true"
|
||||
:disabled="getSelect==0">{{`确定(${getSelect})`}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { doctorArticleList } 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: "已加载全部",
|
||||
},
|
||||
noMore: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
getSelect() {
|
||||
return this.list.reduce((co, item) => {
|
||||
if (item['select']) co++
|
||||
return co
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.init();
|
||||
},
|
||||
onReachBottom() {
|
||||
if (this.noMore) {
|
||||
this.$toast("数据已全部加载");
|
||||
return;
|
||||
}
|
||||
this.page++;
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.page = 1
|
||||
this.getList()
|
||||
},
|
||||
init() {
|
||||
this.list = [];
|
||||
this.page = 1;
|
||||
this.keyword = '';
|
||||
this.loading = true;
|
||||
this.getList(true);
|
||||
},
|
||||
getList(is_stop = false) {
|
||||
if (this.loading2) {
|
||||
return;
|
||||
}
|
||||
this.loading2 = true;
|
||||
doctorArticleList({
|
||||
page: this.page,
|
||||
keyword: this.keyword
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.list = this.page === 1 ? res.data : [...this.list, ...res.data];
|
||||
this.noMore = res.data.length < 20;
|
||||
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;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading2 = false;
|
||||
});
|
||||
},
|
||||
changeSelect(index) {
|
||||
const current = this.list[index]
|
||||
current.select = !current.select
|
||||
this.list.splice(index, 1, current)
|
||||
},
|
||||
submit() {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
const list = this.list.filter(item => item.select)
|
||||
if (list.length < 0) {
|
||||
uni.showToast({
|
||||
title: '请选择发送的文章',
|
||||
icon: 'none',
|
||||
mask: true
|
||||
})
|
||||
return
|
||||
}
|
||||
if (list.length > 1) {
|
||||
uni.showToast({
|
||||
title: '只能选择一篇文章',
|
||||
icon: 'none',
|
||||
mask: true
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.setStorageSync('articleList', list)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #eeeeef;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.select {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border: 2rpx solid #E8E9EB;
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
|
||||
.search {
|
||||
position: relative;
|
||||
z-index: 982;
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
z-index: 982;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: fixed;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
height: 110rpx;
|
||||
background: #FFFFFF;
|
||||
border-top: 2rpx solid #F2F3F5;
|
||||
padding: 12rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
z-index: 999;
|
||||
|
||||
::v-deep button {
|
||||
z-index: 999;
|
||||
background: #6ACDBB;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,228 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="white p-32" v-if="info&&!loading">
|
||||
<view class="flex-row flex-ali-center m-b-48" v-if="info">
|
||||
<u-image width="96rpx" height="96rpx" shape="circle" :src="getAvatar">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<view class="flex-col flex-jus-sp m-l-32">
|
||||
<view class="flex-row flex-ali-center">
|
||||
<d-text :text="info.patient.name" className="content-c fs-32"></d-text>
|
||||
<d-text :text="info.patient.sex==1?'男':'女'" className="tips-c m-l-2"></d-text>
|
||||
<d-text :text="info.age+'岁'" className="tips-c m-l-2"></d-text>
|
||||
</view>
|
||||
<d-text :text="getMobile(info.patient.mobile)"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-b-48 flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="备注" width="85rpx"></d-text>
|
||||
<view class="flex-row flex-ali-center">
|
||||
<d-text width="558" :text="info.remark.remark" className="content-c m-r-16" :lines="1">
|
||||
</d-text>
|
||||
<u-icon name="arrow-right" color="#A7ABB0" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-b-48 flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="分组" width="85rpx"></d-text>
|
||||
<view class="flex-row flex-ali-center flex-jus-end">
|
||||
<d-text :text="getGroup(info.group)" width="558" align="right" className="content-c m-r-16"
|
||||
:lines="1"></d-text>
|
||||
<u-icon name="arrow-right" color="#A7ABB0" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t-2 white" v-if="!loading">
|
||||
<u-tabs :list="tabs" :is-scroll="false" :current="current" @change="current=$event;loading2=true;getList()"
|
||||
bar-width="20" inactive-color="#6C7380"></u-tabs>
|
||||
<view class="p-32" v-if="list[current].length>0&&!loading2">
|
||||
<block v-if="current==0">
|
||||
<view class="flex-col" :class="{'m-b-32':(i+1)!=list[current].length}"
|
||||
v-for="(item,i) in list[current]" :key="i">
|
||||
<d-text text="2022-11-16"></d-text>
|
||||
<view class="flex-row flex-ali-center m-t-16">
|
||||
<d-text text="主诉:"></d-text>
|
||||
<d-text :text="item.main_suit" className="content-c"></d-text>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t08">
|
||||
<d-text width="85rpx" text="诊断:" className=""></d-text>
|
||||
<d-text :text="item.diagnose" className="flex-1 content-c" :lines="1">
|
||||
</d-text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block v-else>
|
||||
<view class="flex-col" v-for="(item,i) in list[current]" :key="i">
|
||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text :text="$u.timeFormat(item.created_at, 'yyyy-mm-dd hh:MM:ss')" color="#6C7380">
|
||||
</d-text>
|
||||
<d-text :text="states[item.status].label" :color="states[item.status].color"></d-text>
|
||||
</view>
|
||||
<view class="m-t-32 flex-col">
|
||||
<view class="flex-row m-b-16">
|
||||
<d-text text="医生" align="justify" width="112"></d-text>
|
||||
<d-text :text="item.doctor_name" className="content-c m-l-48"></d-text>
|
||||
</view>
|
||||
<view class="flex-row m-b-16">
|
||||
<d-text text="诊断" width="112" align="justify"></d-text>
|
||||
<d-text :text="item.clinical_diagnose" className="content-c m-l-48 flex-1" :lines="1">
|
||||
</d-text>
|
||||
</view>
|
||||
<view class="flex-row m-b-16">
|
||||
<d-text text="药品" width="112" align="justify"></d-text>
|
||||
<d-text :text="getDrug(item.drug_name)" className="content-c m-l-48 flex-1" :lines="1"></d-text>
|
||||
</view>
|
||||
<!-- <view class="flex-row">
|
||||
<d-text text="问诊记录" width="112" align="justify"></d-text>
|
||||
<d-text text="点击查看" :color="states[0].color" className="m-l-48"></d-text>
|
||||
</view> -->
|
||||
<!-- <view class="m-t-16 flex-row flex-jus-end">
|
||||
<u-button :throttle-time="0" shape="circle" size="mini"
|
||||
:custom-style="{backgroundColor: 'transparent !important'}" plain :ripple="true">
|
||||
查看详情
|
||||
</u-button>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="flex-row flex-jus-center">
|
||||
<d-empty text="无记录" v-if="!loading2&&list[current].length==0"></d-empty>
|
||||
</view>
|
||||
<d-loading-page :loading="loading2"></d-loading-page>
|
||||
</view>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
patientInfo,
|
||||
recordList,
|
||||
prescripRecord
|
||||
} from '@/api/all.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loading2: true,
|
||||
info: {},
|
||||
tabs: [{
|
||||
name: '就诊记录'
|
||||
}, {
|
||||
name: '处方记录'
|
||||
}],
|
||||
list: {
|
||||
0: [],
|
||||
1: []
|
||||
},
|
||||
states: {
|
||||
3: {
|
||||
color: '#2979FF',
|
||||
label: '待使用'
|
||||
},
|
||||
6: {
|
||||
color: '#A7ABB0',
|
||||
label: '已失效'
|
||||
},
|
||||
4: {
|
||||
color: '#A7ABB0',
|
||||
label: '已使用'
|
||||
}
|
||||
},
|
||||
current: 0,
|
||||
id: -1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getAvatar() {
|
||||
return require(`@/static/image/${this.info.patient.sex%2==0?'nv':'nan'}.png`)
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
this.id = op['id'] || -1
|
||||
this.id > 0 && this.getList()
|
||||
this.id > 0 && this.patientInfo()
|
||||
},
|
||||
methods: {
|
||||
getDrug(obj={}) {
|
||||
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)['drug_name'])
|
||||
}
|
||||
}
|
||||
return drugs.join()
|
||||
},
|
||||
getGroup(list) {
|
||||
let groups = []
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
groups.push(list[i].name)
|
||||
}
|
||||
return groups
|
||||
},
|
||||
getMobile(v) {
|
||||
return v.replace(/^(\d{3})\d{4}(\d+)/, "$1****$2")
|
||||
},
|
||||
patientInfo() {
|
||||
patientInfo({
|
||||
up_id: this.id
|
||||
}).then(res => {
|
||||
if (res.errcode == 0) {
|
||||
this.info = res.data
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
async getList() {
|
||||
let res = {}
|
||||
this.current == 0 && (res = await recordList({
|
||||
up_id: this.id
|
||||
}))
|
||||
this.current == 1 && (res = await prescripRecord({
|
||||
up_id: this.id
|
||||
}))
|
||||
if (res.errcode == 0) {
|
||||
this.list[this.current] = res.data && res.data.list ? res.data.list : res.data || []
|
||||
this.$nextTick(() => {
|
||||
this.loading2 = false
|
||||
})
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.loading2 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.text {
|
||||
width: 122rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
.text:after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -158,10 +158,6 @@ import { sendDoctor } from '../../api/all';
|
||||
this.select = true;
|
||||
this.$go(`patient_select?list=${this.info.patient&&JSON.stringify(this.info.patient)}`)
|
||||
},
|
||||
// goArtcle(){
|
||||
// this.select1 = true;
|
||||
// this.$go(`article_select`)
|
||||
// },
|
||||
confirm(e) {
|
||||
// console.log(e,'time');
|
||||
let time = `${e.month}/${e.day} ${e.hour}:${e.minute}`
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
<template>
|
||||
<view class="change_password">
|
||||
<view class="box">
|
||||
<u-input
|
||||
style="width: 100%"
|
||||
placeholder="当前密码"
|
||||
v-model="value"
|
||||
type="password"
|
||||
></u-input>
|
||||
</view>
|
||||
<view class="box">
|
||||
<u-input
|
||||
style="width: 100%"
|
||||
placeholder="新密码(6-20位)"
|
||||
v-model="pwd1"
|
||||
type="password"
|
||||
></u-input>
|
||||
</view>
|
||||
<view class="box">
|
||||
<u-input
|
||||
style="width: 100%"
|
||||
placeholder="确认新密码"
|
||||
v-model="pwd2"
|
||||
type="password"
|
||||
></u-input>
|
||||
</view>
|
||||
<view class="btn">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
type="primary"
|
||||
:disabled="!disabled"
|
||||
@click="updatePassword"
|
||||
shape="circle"
|
||||
:loading="loading"
|
||||
>确认修改</u-button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { updatePassword } from "@/api/all.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value: null,
|
||||
pwd1: null,
|
||||
pwd2: null,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
disabled() {
|
||||
return this.value && this.pwd1 && this.pwd2;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async updatePassword() {
|
||||
if (this.pwd1!==this.pwd2) {
|
||||
this.$toast("两次密码不一致");
|
||||
return;
|
||||
}
|
||||
if (this.loading) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await updatePassword({
|
||||
password_old: this.value,
|
||||
password_new: this.pwd1,
|
||||
});
|
||||
if (res.errcode != -1) {
|
||||
this.$toast("修改成功");
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
}
|
||||
} catch (error) {
|
||||
this.$toast("系统错误");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.change_password {
|
||||
height: 100vh;
|
||||
background: #f8fafc;
|
||||
padding-top: 20px;
|
||||
.box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
padding: 0 60rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.btn {
|
||||
margin: 0 32rpx;
|
||||
margin-top: 80rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,42 +0,0 @@
|
||||
<template>
|
||||
<view class="">
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: 0,
|
||||
info: {},
|
||||
page: 1,
|
||||
record: "",
|
||||
|
||||
};
|
||||
},
|
||||
onLoad(op) {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// this.a = setInterval(() => {
|
||||
// this.messageList()
|
||||
// }, 1000)
|
||||
},
|
||||
destroyed() {
|
||||
// 取消监听键盘高度变化事件
|
||||
uni.offKeyboardHeightChange();
|
||||
// clearInterval(this.a)
|
||||
// this.a = null
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -258,11 +258,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 审方详情页:老 Yii examine/detail、examine/use-way、examine/verify 已迁移到 xk-api /pharmacist-examine/*
|
||||
import {
|
||||
getPassed,
|
||||
prescripRecordDetail,
|
||||
getUseWay
|
||||
} from "@/api/all.js";
|
||||
getExamineDetail,
|
||||
getExamineUseWay,
|
||||
verifyExamine
|
||||
} from "@/api/pharmacistExamine.js";
|
||||
import { unwrapXkApi } from "@/utils/api-response.js";
|
||||
import { withWxKey } from '@/utils/wxListKey.js'
|
||||
export default {
|
||||
data() {
|
||||
@@ -298,16 +300,14 @@
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
// console.log('12')
|
||||
// console.log(e, "id");
|
||||
this.id = e.id
|
||||
this.prescription_no = e.prescription_no
|
||||
this.getInfo()
|
||||
getUseWay({
|
||||
store_id: uni.getStorageSync('store_id') || 11001,
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
const useWay = ["无", ...(res.data.map(it => it.name))]
|
||||
// 用法字典(xk-api):下标 0 占位「无」,药材 order 字段按下标映射煎法名称
|
||||
getExamineUseWay().then((res) => {
|
||||
const { ok, payload } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
const useWay = ["无", ...(payload.map(it => it.name))]
|
||||
uni.setStorageSync('useWay', JSON.stringify(useWay))
|
||||
this.useWay = useWay
|
||||
}
|
||||
@@ -322,21 +322,22 @@
|
||||
// 如果url包含http,则返回url,反之加上 data:image/jpeg;base64,
|
||||
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
|
||||
},
|
||||
/** 驳回:examine/verify 已迁 xk-api,驳回原因必填,成功后刷新详情展示最新状态 */
|
||||
confirm() {
|
||||
getPassed({
|
||||
verifyExamine({
|
||||
prescription_no: this.prescription_no,
|
||||
status: 2,
|
||||
reject_reason: this.reject_reason,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
const { ok, payload, message } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
this.show = false
|
||||
this.loading = true;
|
||||
this.isshow = false
|
||||
this.getInfo() && this.$go(1, 4)
|
||||
this.$toast(res.data[0]);
|
||||
this.getInfo()
|
||||
this.$toast((payload && payload[0]) || '已驳回');
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.$toast(message);
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
@@ -370,37 +371,38 @@
|
||||
})
|
||||
return withWxKey(normalized, 'id', 'rp')
|
||||
},
|
||||
/** 详情:examine/detail 已迁 xk-api,content 后端已解码,签名图/公章字段名与老接口一致 */
|
||||
getInfo() {
|
||||
prescripRecordDetail({
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
getExamineDetail({
|
||||
prescription_no: this.prescription_no
|
||||
}).then((res) => {
|
||||
// console.log(res, 'deta');
|
||||
|
||||
if (res.errcode == 0) {
|
||||
const content = res.data.content
|
||||
const { ok, payload, message } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
const content = payload.content
|
||||
content.repice = this.normalizeRepice(content.repice)
|
||||
this.infoList = content
|
||||
this.rpList = res.data.pharmacistInfo
|
||||
this.lists = res.data
|
||||
// console.log(this.infoList, 'info');
|
||||
this.rpList = payload.pharmacistInfo
|
||||
this.lists = payload
|
||||
} else {
|
||||
this.$toast(message);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 通过:examine/verify 已迁 xk-api(status=1),后端连带订单 ERP 同步与站内通知 */
|
||||
pass(e) {
|
||||
this.status = e
|
||||
getPassed({
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
verifyExamine({
|
||||
prescription_no: this.prescription_no,
|
||||
status: this.status
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
const { ok, message } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
this.show = false
|
||||
this.$toast('审核成功')
|
||||
this.getInfo()
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.$toast(message);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom p-2">
|
||||
<view
|
||||
class="p-32 white b-r-8 flex-col"
|
||||
v-for="(item, i) in list"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp">
|
||||
<view class="flex-row flex-ali-center flex-jus-sp">
|
||||
<view style="position: relative">
|
||||
<u-image
|
||||
width="80rpx"
|
||||
height="80rpx"
|
||||
shape="circle"
|
||||
:src="item.user.avatar || '../../static/image/djr.png'"
|
||||
>
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<!-- <u-badge :offset="[0,-8]" :count="2"></u-badge> -->
|
||||
</view>
|
||||
<view class="m-l-2 flex-col flex-jus-sp">
|
||||
<view class="flex-row flex-ali-center">
|
||||
<d-text
|
||||
:text="item.user.name || '用户'"
|
||||
className="fs-32 tips-c"
|
||||
></d-text>
|
||||
<d-text :text="timeago(item.last.created_at*1000)" color="#969799" className="m-l-2"></d-text>
|
||||
</view>
|
||||
<d-text :text="getMsg(item.last.content).data.text" className="fs-32 main-c"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<u-button
|
||||
size="mini"
|
||||
shape="circle"
|
||||
:ripple="true"
|
||||
type="primary"
|
||||
@click="sessionIn(item.id)"
|
||||
>接入会话
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-jus-center" v-if="!loading && list.length == 0">
|
||||
<d-empty text="暂无待接入用户" mode="search"></d-empty>
|
||||
</view>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { sessionIn,waitInList } from "@/api/all.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
list: [],
|
||||
info: "",
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.waitInList()
|
||||
},
|
||||
methods: {
|
||||
getMsg(val) {
|
||||
return this.$u.test.jsonString(val)
|
||||
? JSON.parse(val)
|
||||
: {
|
||||
type: "text",
|
||||
data: {
|
||||
text: val,
|
||||
},
|
||||
};
|
||||
},
|
||||
timeago(dateTimeStamp) {
|
||||
let result = "";
|
||||
var minute = 1000 * 60; //把分,时,天,周,半个月,一个月用毫秒表示
|
||||
var hour = minute * 60;
|
||||
var day = hour * 24;
|
||||
var week = day * 7;
|
||||
var halfamonth = day * 15;
|
||||
var month = day * 30;
|
||||
var now = new Date().getTime(); //获取当前时间毫秒
|
||||
console.log(now);
|
||||
var diffValue = now - dateTimeStamp; //时间差
|
||||
|
||||
if (diffValue < 0) {
|
||||
return;
|
||||
}
|
||||
var minC = diffValue / minute; //计算时间差的分,时,天,周,月
|
||||
var hourC = diffValue / hour;
|
||||
var dayC = diffValue / day;
|
||||
var weekC = diffValue / week;
|
||||
var monthC = diffValue / month;
|
||||
if (monthC >= 1 && monthC <= 3) {
|
||||
result = " " + parseInt(monthC) + "月前";
|
||||
} else if (weekC >= 1 && weekC <= 3) {
|
||||
result = " " + parseInt(weekC) + "周前";
|
||||
} else if (dayC >= 1 && dayC <= 6) {
|
||||
result = " " + parseInt(dayC) + "天前";
|
||||
} else if (hourC >= 1 && hourC <= 23) {
|
||||
result = " " + parseInt(hourC) + "小时前";
|
||||
} else if (minC >= 1 && minC <= 59) {
|
||||
result = " " + parseInt(minC) + "分钟前";
|
||||
} else if (diffValue >= 0 && diffValue <= minute) {
|
||||
result = "刚刚";
|
||||
} else {
|
||||
var datetime = new Date();
|
||||
datetime.setTime(dateTimeStamp);
|
||||
var Nyear = datetime.getFullYear();
|
||||
var Nmonth =
|
||||
datetime.getMonth() + 1 < 10
|
||||
? "0" + (datetime.getMonth() + 1)
|
||||
: datetime.getMonth() + 1;
|
||||
var Ndate =
|
||||
datetime.getDate() < 10
|
||||
? "0" + datetime.getDate()
|
||||
: datetime.getDate();
|
||||
var Nhour =
|
||||
datetime.getHours() < 10
|
||||
? "0" + datetime.getHours()
|
||||
: datetime.getHours();
|
||||
var Nminute =
|
||||
datetime.getMinutes() < 10
|
||||
? "0" + datetime.getMinutes()
|
||||
: datetime.getMinutes();
|
||||
var Nsecond =
|
||||
datetime.getSeconds() < 10
|
||||
? "0" + datetime.getSeconds()
|
||||
: datetime.getSeconds();
|
||||
result = Nyear + "-" + Nmonth + "-" + Ndate;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
async waitInList() {
|
||||
this.loading=true
|
||||
let res = {};
|
||||
res = await waitInList();
|
||||
this.loading=false
|
||||
if (res.errcode == 0) {
|
||||
this.list = res.data;
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
}
|
||||
},
|
||||
sessionIn(id) {
|
||||
console.log(id,'0090')
|
||||
uni.showLoading({
|
||||
title: "接入中...",
|
||||
});
|
||||
sessionIn({
|
||||
ims_id: id,
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.info = res.data;
|
||||
this.waitInList()
|
||||
uni.hideLoading();
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,631 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom">
|
||||
<u-navbar title="查看处方" :is-back="false" title-size="34" title-color="#fff" :background="{
|
||||
background:'linear-gradient(90deg, #6ACDBB 0%, #6ACDBB 93%)'}">
|
||||
</u-navbar>
|
||||
|
||||
<!-- 处方 -->
|
||||
<view class="head">
|
||||
<view class="head_record_top">
|
||||
<view class="record">
|
||||
处方编号:CF20222020202020
|
||||
</view>
|
||||
<view class="record_state">
|
||||
普通
|
||||
</view>
|
||||
</view>
|
||||
<!-- 医院 -->
|
||||
<view class="head_record">
|
||||
<image src="@/static/image/hlwyy.png" mode="" v-show="status===true"></image>
|
||||
<view class="record_yard">
|
||||
<view class="yard">
|
||||
互联网医院
|
||||
</view>
|
||||
<view class="state">
|
||||
处方笺
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 时间 -->
|
||||
<view class="head_record_time">
|
||||
开具日期:2022-10-23
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 信息 -->
|
||||
<view class="my_info">
|
||||
<view class="info">
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
姓名:
|
||||
<text>李悦</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
性别:
|
||||
<text>女</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
年龄:
|
||||
<text>18</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info">
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
科室:
|
||||
<text>中医科</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
电话:
|
||||
<text>18000000000</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info">
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
类别:
|
||||
<text>自费</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info_item">
|
||||
<view class="name">
|
||||
诊断:
|
||||
<text>胆囊结核</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 药品 -->
|
||||
<view class="my_medical">
|
||||
<view class="title">
|
||||
Rp
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="name">
|
||||
<text>复方玄驹胶囊(施强)</text>
|
||||
<text>1盒</text>
|
||||
</view>
|
||||
<view class="details">
|
||||
<text>规格:0.42g*45s</text>
|
||||
<text>用法:饭后口服,每日三次,每次3粒</text>
|
||||
<text>可用天数:5天</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="item">
|
||||
<view class="name">
|
||||
<text>生力雄丸(太极)</text>
|
||||
<text>2盒</text>
|
||||
</view>
|
||||
<view class="details">
|
||||
<text>规格:0.32g*12粒*2板</text>
|
||||
<text>用法:口服,每日三次,每次3粒</text>
|
||||
<text>可用天数:6天</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- 医嘱 -->
|
||||
<view class="doctor_order">
|
||||
<view class="order_info">
|
||||
<view class="title">
|
||||
医嘱:
|
||||
</view>
|
||||
<view class="info">
|
||||
7天一疗程,如无好转再来复查
|
||||
</view>
|
||||
</view>
|
||||
<view class="titles">
|
||||
处方开具已完毕
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 医生 -->
|
||||
<view class="my_doctor">
|
||||
<view class="doctor_info">
|
||||
<view class="info">
|
||||
<text>医师</text>
|
||||
<text class="name">李磊</text>
|
||||
</view>
|
||||
<view class="info">
|
||||
<text>药师审核</text>
|
||||
<text class="name">张三</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="price">
|
||||
价格
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<view class="my_prompt">
|
||||
<view class="title">
|
||||
{{status||'温馨提示,请遵医嘱服药!处方48小时内有效!'}}
|
||||
</view>
|
||||
<!-- <view class="prompt">
|
||||
<text>特别提示</text>
|
||||
<ol>
|
||||
<li>1、处方有效期为3天,请及时取药。</li>
|
||||
<li>2、本处方限于互联网医院使用,自行下载配药不具有处方效力。</li>
|
||||
<li>3、服用3天症状未见好转,建议及时复诊或线下就医。</li>
|
||||
</ol>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
<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="flex-col flex-ali-center p-32">
|
||||
<d-text text="手写签名" className="m-b-48 fs-32" color="#323233"></d-text>
|
||||
<u-image width="414rpx" height="204rpx" shape="circle" :src="sign_image">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<view class="m-t-8">
|
||||
<u-button shape="circle" :custom-style="{heigth:'86rpx',width:'646rpx'}"
|
||||
@click="affirm" :throttle-time="0">确认通过</u-button>
|
||||
</view>
|
||||
<view class="m-t-48"></view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<view class="bottom flex-row flex-jus-sp">
|
||||
<u-button type="primary" shape="circle" :disabled="!sign_image"
|
||||
:custom-style="{heigth:'86rpx',width:'327rpx',backgroundColor: 'transparent !important'}" plain
|
||||
@click="is_reject=true;show=true;" :throttle-time="0" :loading="loading" :ripple="true">拒绝
|
||||
</u-button>
|
||||
<u-button :throttle-time="0" :disabled="!sign_image" :loading="loading" shape="circle"
|
||||
:custom-style="{heigth:'86rpx',width:'327rpx'}" @click="show=true">
|
||||
通过</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getRecordDetail,
|
||||
getUseWay
|
||||
} from '@/api/all.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userInfo: uni.getStorageSync("userInfo") || {},
|
||||
is_reject: false,
|
||||
show: false,
|
||||
id: '',
|
||||
type: 0,
|
||||
loading: true,
|
||||
info: '',
|
||||
sign_image: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// getList() {
|
||||
// return this.info ? this.info.recipe.map(item => {
|
||||
// return {
|
||||
// ...item,
|
||||
// content: item['content'] ? JSON.parse(item['content']) : {}
|
||||
// }
|
||||
// }) : []
|
||||
// }
|
||||
},
|
||||
onLoad(op) {
|
||||
this.id = op['id'];
|
||||
},
|
||||
methods: {
|
||||
async getinfo() {
|
||||
this.loading = false
|
||||
|
||||
let res = {}
|
||||
res = await getRecordDetail({
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
prescription_id: this.id
|
||||
})
|
||||
|
||||
this.info = ''
|
||||
if (res.errcode == 0) {
|
||||
console.log(res, 'xq');
|
||||
this.info = res.data;
|
||||
this.info.patient['age'] = this.$d.getAge(this.info['patient']['id_card'])
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getinfo()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.min-w-140 {
|
||||
min-width: 140rpx;
|
||||
}
|
||||
|
||||
.rp {
|
||||
grid-column-gap: 80rpx;
|
||||
grid-row-gap: 8rpx;
|
||||
}
|
||||
|
||||
.signature {
|
||||
width: 96rpx;
|
||||
height: 44rpx;
|
||||
border-bottom: 1px solid #31353D;
|
||||
font-size: 32rpx;
|
||||
color: #000;
|
||||
|
||||
image {
|
||||
width: 80rpx;
|
||||
height: 38rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
height: calc(110rpx + env(safe-area-inset-bottom));
|
||||
padding: 12rpx 32rpx;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
border-top: 1px solid #F2F3F5;
|
||||
padding-bottom: calc(12rpx + env(safe-area-inset-bottom)) !important;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.hiht {
|
||||
margin: 32rpx 0 calc(32rpx + env(safe-area-inset-bottom)) 0;
|
||||
}
|
||||
|
||||
.head {
|
||||
.head_record_top {
|
||||
width: 710rpx;
|
||||
height: 42rpx;
|
||||
margin: 34rpx auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.record {
|
||||
height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #6C7380;
|
||||
line-height: 33rpx;
|
||||
}
|
||||
|
||||
.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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
image {
|
||||
width: 195rpx;
|
||||
height: 195rpx;
|
||||
position: absolute;
|
||||
left: 38%;
|
||||
top: -50rpx;
|
||||
}
|
||||
|
||||
.record_yard {
|
||||
width: 240rpx;
|
||||
height: 112rpx;
|
||||
text-align: center;
|
||||
|
||||
.yard {
|
||||
width: 240rpx;
|
||||
height: 68rpx;
|
||||
font-size: 48rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
line-height: 56rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
height: 232rpx;
|
||||
margin: 8rpx auto;
|
||||
border-top: 2rpx solid #31353D;
|
||||
border-bottom: 2rpx solid #31353D;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
padding: 16rpx 4rpx;
|
||||
|
||||
.info {
|
||||
width: 710rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.info_item {
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #6C7380;
|
||||
line-height: 40rpx;
|
||||
|
||||
text {
|
||||
display: block;
|
||||
width: 140rpx;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 药品
|
||||
.my_medical {
|
||||
width: 710rpx;
|
||||
height: 492rpx;
|
||||
margin: 32rpx auto;
|
||||
|
||||
.title {
|
||||
height: 44rpx;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC-Semibold, PingFang SC;
|
||||
font-weight: 600;
|
||||
color: #31353D;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
width: 710rpx;
|
||||
height: 228rpx;
|
||||
border-bottom: 2rpx solid #E8E9EB;
|
||||
padding-top: 32rpx;
|
||||
|
||||
.name {
|
||||
width: 710rpx;
|
||||
height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.details {
|
||||
height: 120rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #6C7380;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 医嘱
|
||||
.doctor_order {
|
||||
width: 710rpx;
|
||||
height: 128rpx;
|
||||
margin: 16rpx auto;
|
||||
|
||||
.order_info {
|
||||
width: 710rpx;
|
||||
height: 88rpx;
|
||||
|
||||
.title {
|
||||
height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #6C7380;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.titles {
|
||||
width: 710rpx;
|
||||
height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 医生
|
||||
.my_doctor {
|
||||
width: 710rpx;
|
||||
height: 172rpx;
|
||||
margin: 8rpx auto;
|
||||
border-top: 2rpx solid #31353D;
|
||||
border-bottom: 2rpx solid #31353D;
|
||||
padding: 32rpx 4rpx;
|
||||
|
||||
.doctor_info {
|
||||
width: 710rpx;
|
||||
height: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.info {
|
||||
width: 222rpx;
|
||||
|
||||
text {
|
||||
height: 34rpx;
|
||||
line-height: 28rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
}
|
||||
|
||||
.name {
|
||||
width: 106rpx;
|
||||
height: 44rpx;
|
||||
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;
|
||||
height: 44rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 温馨提示
|
||||
.my_prompt {
|
||||
width: 710rpx;
|
||||
height: 304rpx;
|
||||
margin: 0 auto 30rpx;
|
||||
padding: 0 4rpx;
|
||||
|
||||
.title {
|
||||
width: 710rpx;
|
||||
height: 60rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
line-height: 60rpx;
|
||||
margin: 32rpx 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
</style>
|
||||
@@ -20,7 +20,7 @@
|
||||
<view class="m-l-4 flex-1">
|
||||
<view class="flex-row flex-ali-center flex-jus-sp">
|
||||
<d-text text="就诊" className="fs-36 content-c"></d-text>
|
||||
<d-text :text="$u.timeFormat(prescription.patients.updated_at, 'yyyy-mm-dd hh:MM')"></d-text>
|
||||
<d-text :text="prescription.patients.updated_at"></d-text>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-32">
|
||||
<d-text text="姓名"></d-text>
|
||||
@@ -40,7 +40,7 @@
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-48">
|
||||
<d-text text="开具" className="fs-36 content-c"></d-text>
|
||||
<d-text :text="$u.timeFormat(prescription.created_at, 'yyyy-mm-dd hh:MM')"></d-text>
|
||||
<d-text :text="prescription.created_at"></d-text>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-32">
|
||||
<d-text text="处方编号"></d-text>
|
||||
@@ -65,11 +65,11 @@
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-16">
|
||||
<d-text text="开具时间"></d-text>
|
||||
<d-text :text="$u.timeFormat(prescription.created_at, 'yyyy-mm-dd hh:MM')"></d-text>
|
||||
<d-text :text="prescription.created_at"></d-text>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-48">
|
||||
<d-text text="审核" className="fs-36 content-c"></d-text>
|
||||
<d-text :text="$u.timeFormat(prescription.pharmacist_view_time, 'yyyy-mm-dd hh:MM')"></d-text>
|
||||
<d-text :text="prescription.pharmacist_view_time"></d-text>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp m-t-32">
|
||||
<d-text text="审核状态"></d-text>
|
||||
@@ -92,9 +92,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 处方溯源页:老 Yii examine/source 已迁移到 xk-api /pharmacist-examine/source
|
||||
import {
|
||||
source,
|
||||
} from '@/api/all.js'
|
||||
getExamineSource,
|
||||
} from '@/api/pharmacistExamine.js'
|
||||
import {
|
||||
unwrapXkApi
|
||||
} from '@/utils/api-response.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -114,22 +118,25 @@
|
||||
},
|
||||
onLoad(op) {
|
||||
this.source({
|
||||
prescription_id: op['prescription_id'],
|
||||
prescription_no: op['prescription_no'],
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
source(data) {
|
||||
source(data).then(res => {
|
||||
if (res.errcode == 0) {
|
||||
this.info = res.data;
|
||||
this.prescription = res.data.prescription
|
||||
this.type = res.data.type
|
||||
// this.info['age'] = this.$d.getAge(this.info.prescription['patients']['id_card'])
|
||||
getExamineSource(data).then(res => {
|
||||
const { ok, payload, message } = unwrapXkApi(res)
|
||||
if (ok) {
|
||||
// 时间字段后端已格式化为字符串,截取到分钟;patients 兜底空对象防模板取值报错
|
||||
const prescription = payload.prescription || {}
|
||||
prescription.patients = prescription.patients || {}
|
||||
prescription.created_at = (prescription.created_at || '').slice(0, 16)
|
||||
prescription.patients.updated_at = (prescription.patients.updated_at || '').slice(0, 16)
|
||||
this.info = payload;
|
||||
this.prescription = prescription
|
||||
this.type = payload.type
|
||||
this.loading = false
|
||||
} else {
|
||||
this.$toast(res.msg);
|
||||
this.$toast(message);
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom p-2">
|
||||
<view class="search p-row-32">
|
||||
<u-search
|
||||
placeholder="请输入患者姓名、标签"
|
||||
v-model="keyword"
|
||||
@change="change"
|
||||
:show-action="false"
|
||||
></u-search>
|
||||
</view>
|
||||
<!-- <view class="p-32 white b-r-8 flex-col" v-if="!loading&&list.length>0"> -->
|
||||
<view
|
||||
class="p-32 white b-r-8 flex-col"
|
||||
v-for="(item, i) in list"
|
||||
:key="item.id"
|
||||
>
|
||||
<view class="flex-row flex-ali-center flex-jus-sp">
|
||||
<view class="flex-row flex-ali-center flex-jus-sp">
|
||||
<view style="position: relative">
|
||||
<u-image
|
||||
width="80rpx"
|
||||
height="80rpx"
|
||||
shape="circle"
|
||||
:src="item.avator || '../../static/image/djr.png'"
|
||||
>
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="m-l-2 flex-col flex-jus-sp">
|
||||
<view class="flex-row flex-ali-center">
|
||||
<d-text
|
||||
:text="item.name"
|
||||
className="fs-32 "
|
||||
color="#31353D"
|
||||
></d-text>
|
||||
<d-text
|
||||
:text="item.title"
|
||||
color="#31353D"
|
||||
className="m-l-2"
|
||||
></d-text>
|
||||
</view>
|
||||
<d-text
|
||||
:text="`${item.hospital}(${item.yard}) ${item.depart}`"
|
||||
color="#6C7380"
|
||||
className="fs-28"
|
||||
></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<view>
|
||||
<u-button
|
||||
size="mini"
|
||||
shape="circle"
|
||||
:ripple="true"
|
||||
type="primary"
|
||||
@click="select(item)"
|
||||
>选择
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-jus-center" v-if="!loading && list.length == 0">
|
||||
<d-empty text="暂无医生" mode="search"></d-empty>
|
||||
</view>
|
||||
<u-loadmore
|
||||
:status="status"
|
||||
:load-text="loadText"
|
||||
v-if="!loading && list.length > 0"
|
||||
/>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { doctorLists } from "@/api/all.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
page: 1,
|
||||
keyword: "",
|
||||
loading: true,
|
||||
loading2: false,
|
||||
status: "loading",
|
||||
loadText: {
|
||||
loadmore: "轻轻上拉",
|
||||
loading: "努力加载中",
|
||||
nomore: "已加载全部",
|
||||
},
|
||||
noMore: false,
|
||||
};
|
||||
},
|
||||
// onLoad(op) {
|
||||
// this.loading = false
|
||||
// this.list = op['list'] ? JSON.parse(op['list']) : []
|
||||
// },
|
||||
|
||||
onShow() {
|
||||
this.init();
|
||||
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.init();
|
||||
},
|
||||
onReachBottom() {
|
||||
if (this.noMore) {
|
||||
this.$toast("数据已全部加载");
|
||||
return;
|
||||
}
|
||||
this.page++;
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
select(item) {
|
||||
uni.showModal({
|
||||
confirmText:'确定发送',
|
||||
content:`发送 ${item.name} 医生的名片到当前聊天`,
|
||||
success: function (res) {
|
||||
if (res.confirm) {
|
||||
uni.$emit("newAddPv", item);
|
||||
uni.navigateBack({
|
||||
delta: 1, //返回层数,2则上上页
|
||||
});
|
||||
} else if (res.cancel) {
|
||||
console.log("用户点击取消");
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
change() {
|
||||
this.list = [];
|
||||
this.page = 1;
|
||||
uni.showLoading({
|
||||
title: "搜索中...",
|
||||
});
|
||||
uni.$u.debounce(this.getList, 600);
|
||||
},
|
||||
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;
|
||||
doctorLists({
|
||||
page: this.page,
|
||||
keyword: this.keyword,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.list =
|
||||
this.page === 1
|
||||
? res.data.list
|
||||
: [...this.list, ...res.data.list];
|
||||
this.noMore = res.data.list.length < 20;
|
||||
if (this.noMore) {
|
||||
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;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
this.loading2 = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.search {
|
||||
position: relative;
|
||||
z-index: 982;
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@ import { platformAdminApi } from '@/api/businessApi.js';
|
||||
|
||||
const BUSINESS_PACKAGE_ROOT = '/subPackages/sub_business_shared';
|
||||
const PLATFORM_HOME = '/subPackages/sub_platform_admin/home/index';
|
||||
const PLATFORM_FEATURES = '/subPackages/sub_platform_admin/features/index';
|
||||
const PLATFORM_MY = '/subPackages/sub_platform_admin/my/index';
|
||||
|
||||
export function getPlatformAdminContext() {
|
||||
@@ -29,6 +30,7 @@ export function getPlatformAdminContext() {
|
||||
},
|
||||
tabList: [
|
||||
{ text: '工作台', iconPath: 'home', selectedIconPath: 'home-fill', pagePath: PLATFORM_HOME },
|
||||
{ text: '功能', iconPath: 'grid', selectedIconPath: 'grid-fill', pagePath: PLATFORM_FEATURES },
|
||||
{ text: '我的', iconPath: 'account', selectedIconPath: 'account-fill', pagePath: PLATFORM_MY },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
<view class="clinic-body" :style="bodyStyle">
|
||||
<slot />
|
||||
</view>
|
||||
<!-- 高度显式传 rpx(100 与 body 预留一致):默认 50px 固定像素在宽屏机型会与内部 rpx 定位的 icon/文字冲突挤压 -->
|
||||
<u-tabbar
|
||||
v-if="showTabbar"
|
||||
:value="tabIndex"
|
||||
:list="tabList"
|
||||
:height="100"
|
||||
active-color="#6ACDBB"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
39
subPackages/sub_platform_admin/features/index.vue
Normal file
39
subPackages/sub_platform_admin/features/index.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<business-page-layout title="功能" :show-tabbar="true" :tab-index="1">
|
||||
<workbench-features ref="features" :api="featuresApi"></workbench-features>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 平台管理员端「功能」tab 薄壳页
|
||||
* 壳页只负责:套本端 layout(navbar/tabbar 配置)+ 注入本端角色接口,
|
||||
* 搜索/分组滑动/长按收藏/使用上报等全部逻辑在主包共享组件 workbench-features(四端复用)
|
||||
*/
|
||||
import BusinessPageLayout from '@/subPackages/sub_platform_admin/components/business-page-layout.vue'
|
||||
import {
|
||||
getPlatformAdminWorkbenchEntries,
|
||||
getPlatformAdminEntryUserData,
|
||||
togglePlatformAdminFavoriteEntry,
|
||||
reportPlatformAdminEntryUsage,
|
||||
} from '@/api/platformAdmin.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout },
|
||||
data() {
|
||||
return {
|
||||
/** 注入共享组件的本端接口集合(统一返回 { res, data, ok }) */
|
||||
featuresApi: {
|
||||
getEntries: getPlatformAdminWorkbenchEntries,
|
||||
getUserData: getPlatformAdminEntryUserData,
|
||||
toggleFavorite: togglePlatformAdminFavoriteEntry,
|
||||
reportUsage: reportPlatformAdminEntryUsage,
|
||||
},
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 从功能跳转的页面返回时刷新常用/最近使用(首屏加载由组件自身 mounted 完成,此时 ref 还不存在)
|
||||
if (this.$refs.features) this.$refs.features.refreshUserData()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
<business-page-layout title="工作台" :show-tabbar="true" :tab-index="0">
|
||||
<view class="workspace-container">
|
||||
|
||||
<!-- 极简流线型顶部个人信息区 -->
|
||||
<!-- 极简流线型顶部个人信息区:右侧日期卡补齐视觉平衡(此前右侧空白失衡) -->
|
||||
<view class="hero-section">
|
||||
<view class="brand-info-wrap">
|
||||
<view class="brand-text">
|
||||
@@ -10,37 +10,65 @@
|
||||
<text class="store-name">{{ displayName }}</text>
|
||||
<text class="store-sub">欢迎回来,今天也要高效工作</text>
|
||||
</view>
|
||||
<view class="date-chip">
|
||||
<text class="date-day">{{ todayDay }}</text>
|
||||
<text class="date-month">{{ todayMonth }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷服务区:多彩微拟物质感 (复用样式逻辑) -->
|
||||
<view class="content-wrapper">
|
||||
<view v-for="section in sections" :key="section.key" class="section-panel">
|
||||
<view class="panel-header">
|
||||
<text class="panel-title">{{ section.title }}</text>
|
||||
<!-- 平台看板:hero 下沉浮卡,待办审核数 + 今日订单概览(统一工作台看板规格) -->
|
||||
<view class="dashboard-wrapper">
|
||||
<view class="dashboard-card">
|
||||
<view class="stat-item" @click="go({ path: '/subPackages/sub_platform_admin/store-input/audit/index' })">
|
||||
<text class="stat-label">待审核门店</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value" :class="{ warn: dashboard.pending_store_audit > 0 }">{{ dashboard.pending_store_audit }}</text>
|
||||
<text class="stat-unit">家</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-grid">
|
||||
<view
|
||||
v-for="item in section.items"
|
||||
:key="item.key"
|
||||
class="service-card"
|
||||
hover-class="service-card-hover"
|
||||
@click="go(item)"
|
||||
>
|
||||
<view class="service-icon-box" :style="{ background: item.theme.bg }">
|
||||
<!-- 使用 uView 1.x 的 u-icon 组件 -->
|
||||
<u-icon :name="item.icon" size="38" :color="item.theme.color"></u-icon>
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<text class="service-name">{{ item.label }}</text>
|
||||
<text class="service-desc">{{ item.desc || '快捷进入' }}</text>
|
||||
</view>
|
||||
<view class="stat-item" @click="go({ path: '/subPackages/sub_platform_admin/doctor-input/audit/index' })">
|
||||
<text class="stat-label">待审核医生</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value" :class="{ warn: dashboard.pending_doctor_audit > 0 }">{{ dashboard.pending_doctor_audit }}</text>
|
||||
<text class="stat-unit">人</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item" @click="go({ path: '/subPackages/sub_platform_admin/withdrawal/audit/index' })">
|
||||
<text class="stat-label">待审核提现</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value" :class="{ warn: dashboard.pending_withdrawal_count > 0 }">{{ dashboard.pending_withdrawal_count }}</text>
|
||||
<text class="stat-unit">笔</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">今日订单</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value">{{ dashboard.today_order_count }}</text>
|
||||
<text class="stat-unit">单</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-label">今日交易额</text>
|
||||
<view class="stat-value-group">
|
||||
<text class="stat-value highlight">{{ dashboard.today_order_amount }}</text>
|
||||
<text class="stat-unit">元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容区:个人功能区(最近使用 / 我的常用 / 更多功能,全部入口收敛到功能 tab) -->
|
||||
<view class="content-wrapper">
|
||||
<workbench-user-entries
|
||||
:recents="recentItems"
|
||||
:favorites="favItems"
|
||||
:card-long="cardLong"
|
||||
@item-click="onEntryTap"
|
||||
@more="goFeatures"
|
||||
></workbench-user-entries>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
@@ -49,165 +77,114 @@
|
||||
import BusinessPageLayout from '@/subPackages/sub_platform_admin/components/business-page-layout.vue'
|
||||
import { getPlatformAdminContext } from '@/subPackages/sub_platform_admin/common/context.js'
|
||||
import { navigateToInputForm } from '@/subPackages/sub_salesperson/common/inputNavigation.js'
|
||||
import platformAdminApi from '@/api/platformAdmin.js'
|
||||
|
||||
const ROOT = '/subPackages/sub_platform_admin'
|
||||
const SALESPERSON_ROOT = '/subPackages/sub_salesperson'
|
||||
import { getPlatformAdminDashboardStats, getPlatformAdminEntryUserData, reportPlatformAdminEntryUsage } from '@/api/platformAdmin.js'
|
||||
import { normalizeEntry } from '@/utils/workbenchEntries.js'
|
||||
import { getCardStyle } from '@/utils/workbenchCardPref.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout },
|
||||
provide() { return { businessContext: getPlatformAdminContext() } },
|
||||
data() {
|
||||
const now = new Date()
|
||||
return {
|
||||
displayName: '',
|
||||
warehouseMenus: [],
|
||||
// 预定义的全局主题池,用于动态分配没有明确指定主题的项
|
||||
globalThemes: [
|
||||
{ bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' }, // 橙金
|
||||
{ bg: 'linear-gradient(135deg, #EFF6FF, #DBEAFE)', color: '#3B82F6' }, // 科技蓝
|
||||
{ bg: 'linear-gradient(135deg, #FAF5FF, #F3E8FF)', color: '#A855F7' }, // 优雅紫
|
||||
{ bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' }, // 安全绿
|
||||
]
|
||||
/** hero 右侧日期卡:日 + 月,纯展示用于视觉平衡 */
|
||||
todayDay: String(now.getDate()),
|
||||
todayMonth: (now.getMonth() + 1) + '月',
|
||||
/** 平台看板数据(接口返回前展示 0,避免布局跳动) */
|
||||
dashboard: {
|
||||
pending_store_audit: 0,
|
||||
pending_doctor_audit: 0,
|
||||
pending_withdrawal_count: 0,
|
||||
today_order_count: 0,
|
||||
today_order_amount: 0,
|
||||
},
|
||||
/** 我的常用/最近使用原始入口(归一化后存储,行为装饰在 computed 注入保证角标响应式) */
|
||||
favRaw: [],
|
||||
recentRaw: [],
|
||||
/** 卡片形态偏好:true 长卡一行两个 / false 金刚区宫格(我的页可切换,存本地) */
|
||||
cardLong: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
sections() {
|
||||
const shared = '/subPackages/sub_business_shared'
|
||||
const root = '/subPackages/sub_platform_admin'
|
||||
|
||||
return [
|
||||
{
|
||||
key: 'finance',
|
||||
title: '财务管理',
|
||||
items: [
|
||||
{
|
||||
key: 'withdrawal', label: '提现管理', desc: '提现流水查询', path: `${shared}/withdrawal/index`,
|
||||
icon: 'red-packet-fill', // uView icon
|
||||
theme: { bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' } // 橙金
|
||||
},
|
||||
{
|
||||
key: 'withdrawal-audit', label: '提现审核', desc: '处理提现申请', path: `${root}/withdrawal/audit/index`,
|
||||
icon: 'order',
|
||||
theme: { bg: 'linear-gradient(135deg, #FEE2E2, #FECACA)', color: '#EF4444' } // 警戒红
|
||||
},
|
||||
{
|
||||
key: 'reconciliation', label: '对账单', desc: '财务对账明细', path: `${shared}/reconciliation/index`,
|
||||
icon: 'file-text-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #EFF6FF, #DBEAFE)', color: '#3B82F6' } // 科技蓝
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'business',
|
||||
title: '业务管理',
|
||||
items: [
|
||||
{
|
||||
key: 'order', label: '商品订单', desc: '全平台订单概览', path: `${shared}/order/index`,
|
||||
icon: 'bag-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' } // 安全绿
|
||||
},
|
||||
{
|
||||
key: 'user-patient', label: '会员管理', desc: '微信用户与就诊人', path: `${shared}/user-patient/index`,
|
||||
icon: 'account',
|
||||
theme: { bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' }
|
||||
},
|
||||
{
|
||||
key: 'store-manage', label: '门店管理', desc: '诊所列表与价格配置', path: `${root}/store/index`,
|
||||
icon: 'home-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #ECFEFF, #CFFAFE)', color: '#06B6D4' }
|
||||
},
|
||||
{
|
||||
key: 'salesperson-manage', label: '推广员管理', desc: '全平台推广员', path: '/subPackages/sub_salesperson_manage/index?mode=platform',
|
||||
icon: 'account-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #FAF5FF, #F3E8FF)', color: '#A855F7' }
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'warehouse',
|
||||
title: '仓库管理',
|
||||
// 动态加载的仓库数据,在 onShow 中会进行加工
|
||||
items: this.warehouseMenus.length ? this.warehouseMenus : [
|
||||
// 提供一个默认的占位符,防止异步加载完成前显示空白
|
||||
{
|
||||
key: 'warehouse-loading', label: '总仓库', desc: '库存管理中心', path: '/subPackages/sub_business_shared/warehouse/index',
|
||||
icon: 'car-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #F3F4F6, #E5E7EB)', color: '#6B7280' } // 占位灰
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'input-audit',
|
||||
title: '信息审核',
|
||||
items: [
|
||||
{
|
||||
key: 'store-audit', label: '门店审核', desc: '新入门店审批', path: `${root}/store-input/audit/index`,
|
||||
icon: 'home-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #FAF5FF, #F3E8FF)', color: '#A855F7' } // 优雅紫
|
||||
},
|
||||
{
|
||||
key: 'doctor-audit', label: '医生审核', desc: '入驻医生资质', path: `${root}/doctor-input/audit/index`,
|
||||
icon: 'account-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #EFF6FF, #DBEAFE)', color: '#3B82F6' } // 科技蓝
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'input-create',
|
||||
title: '信息录入',
|
||||
items: [
|
||||
{
|
||||
key: 'store-list', label: '门店列表', desc: '平台门店档案', path: `${SALESPERSON_ROOT}/store-input/index`,
|
||||
icon: 'grid-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #ECFEFF, #CFFAFE)', color: '#06B6D4' } // 青色
|
||||
},
|
||||
{
|
||||
key: 'store-form', label: '门店录入', desc: '新增线下门店', path: `${SALESPERSON_ROOT}/store-input/form`, inputType: 'store', isForm: true,
|
||||
icon: 'plus-square-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' } // 绿色
|
||||
},
|
||||
{
|
||||
key: 'doctor-list', label: '医生列表', desc: '平台医生名册', path: `${SALESPERSON_ROOT}/doctor-input/index`,
|
||||
icon: 'list-dot',
|
||||
theme: { bg: 'linear-gradient(135deg, #FFFBEB, #FEF3C7)', color: '#D97706' } // 琥珀色
|
||||
},
|
||||
{
|
||||
key: 'doctor-form', label: '医生录入', desc: '新增合作医生', path: `${SALESPERSON_ROOT}/doctor-input/form`, inputType: 'doctor', isForm: true,
|
||||
icon: 'plus-circle-fill',
|
||||
theme: { bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' } // 橙金
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
/** 我的常用(角标/表单预填等行为装饰在这里注入,保证看板数据返回后角标响应式更新) */
|
||||
favItems() {
|
||||
return this.favRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
/** 最近使用 */
|
||||
recentItems() {
|
||||
return this.recentRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
const user = uni.getStorageSync('platform_admin_user') || {}
|
||||
this.displayName = user.nick_name || '平台管理员'
|
||||
|
||||
// 加载仓库数据
|
||||
platformAdminApi.getWarehouseProductTypeOptions().then(w => {
|
||||
const options = Array.isArray(w.data) ? w.data : []
|
||||
if (!options.length) return
|
||||
|
||||
this.warehouseMenus = options.map((opt, index) => {
|
||||
const value = opt.value != null ? opt.value : opt.id
|
||||
const label = opt.label || opt.name || '仓库'
|
||||
// 为动态仓库分配不同的主题色
|
||||
const theme = this.globalThemes[index % this.globalThemes.length]
|
||||
|
||||
return {
|
||||
key: 'warehouse-' + value,
|
||||
label,
|
||||
desc: `${label}库存管理`, // 增强描述
|
||||
icon: 'car-fill', // 仓库类目统一使用 uView 的车/物流图标
|
||||
theme: theme,
|
||||
path: `/subPackages/sub_business_shared/warehouse/index?type=${encodeURIComponent(value)}&label=${encodeURIComponent(label)}`,
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
// 卡片形态偏好每次 onShow 重读:我的页切换后返回立即生效
|
||||
this.cardLong = getCardStyle() === 'long'
|
||||
this.loadDashboard()
|
||||
this.loadUserEntries()
|
||||
},
|
||||
methods: {
|
||||
/** 拉取我的常用 + 最近使用:失败静默留空(首页有引导条 + 更多功能卡兜底) */
|
||||
async loadUserEntries() {
|
||||
try {
|
||||
const wrap = await getPlatformAdminEntryUserData()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.favRaw = this.normalizeList(wrap.data.favorites)
|
||||
this.recentRaw = this.normalizeList(wrap.data.recents)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('常用/最近使用加载失败', e)
|
||||
}
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与功能页同一映射) */
|
||||
normalizeList(list) {
|
||||
if (!Array.isArray(list)) return []
|
||||
const items = []
|
||||
list.forEach((entry) => {
|
||||
const item = normalizeEntry(entry)
|
||||
if (item) items.push(item)
|
||||
})
|
||||
return items
|
||||
},
|
||||
/** 按 code 注入本端行为:审核类入口待办角标、录入表单预填标记 */
|
||||
decorateItem(item) {
|
||||
const badgeMap = {
|
||||
platform_withdrawal_audit: this.dashboard.pending_withdrawal_count,
|
||||
platform_store_audit: this.dashboard.pending_store_audit,
|
||||
platform_doctor_audit: this.dashboard.pending_doctor_audit,
|
||||
}
|
||||
const formMap = { platform_store_form: 'store', platform_doctor_form: 'doctor' }
|
||||
const next = { ...item }
|
||||
if (badgeMap[item.key] != null) {
|
||||
next.badge = badgeMap[item.key]
|
||||
}
|
||||
if (formMap[item.key]) {
|
||||
next.isForm = true
|
||||
next.inputType = formMap[item.key]
|
||||
}
|
||||
return next
|
||||
},
|
||||
/** 拉取平台看板统计:失败静默保持 0,不阻塞首页其他模块 */
|
||||
async loadDashboard() {
|
||||
try {
|
||||
const wrap = await getPlatformAdminDashboardStats()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.dashboard = { ...this.dashboard, ...wrap.data }
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('看板统计加载失败', e)
|
||||
}
|
||||
},
|
||||
/** 入口点击:静默上报使用(保持最近使用新鲜)→ 表单预填/普通跳转 */
|
||||
onEntryTap(item) {
|
||||
reportPlatformAdminEntryUsage(item.key).catch(() => {})
|
||||
this.go(item)
|
||||
},
|
||||
/** 更多功能/空态引导:切到功能 tab(tabbar 页统一走 reLaunch) */
|
||||
goFeatures() {
|
||||
uni.reLaunch({ url: '/subPackages/sub_platform_admin/features/index' })
|
||||
},
|
||||
go(item) {
|
||||
if (item.isForm && item.inputType) {
|
||||
navigateToInputForm(item.path, item.inputType)
|
||||
@@ -278,78 +255,93 @@ export default {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ================= 主体内容区:快捷服务 ================= */
|
||||
/* hero 右侧日期卡:毛玻璃白底小方卡,与推广员端二维码位同规格 */
|
||||
.date-chip {
|
||||
width: 110rpx;
|
||||
height: 110rpx;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.date-day {
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
line-height: 1.1;
|
||||
font-family: "DIN Alternate", sans-serif;
|
||||
}
|
||||
|
||||
.date-month {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
/* ================= 平台看板:hero 下沉浮白卡(统一工作台看板规格) ================= */
|
||||
.dashboard-wrapper {
|
||||
margin: -30rpx 30rpx 0;
|
||||
position: relative;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 20rpx;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
row-gap: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.stat-value-group {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
/* 数值:DIN 数字字体统一看板视觉语言 */
|
||||
.stat-value {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B;
|
||||
font-family: "DIN Alternate", sans-serif;
|
||||
}
|
||||
|
||||
.stat-value.highlight {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.stat-value.warn {
|
||||
color: #F97316;
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
/* ================= 主体内容区:个人功能区(分区/宫格样式在 workbench-user-entries 组件内) ================= */
|
||||
.content-wrapper {
|
||||
margin-top: 30rpx; /* 轻微向上偏移,与顶部区域产生连接感 */
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section-panel {
|
||||
margin: 0 30rpx 30rpx; /* 增加底部间距 */
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1E293B; /* 深石板灰 */
|
||||
}
|
||||
|
||||
/* 采用与之前页面一致的宫格卡片布局 */
|
||||
.service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.service-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 28rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.service-card-hover {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.service-icon-box {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 22rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
box-shadow: inset 0 2rpx 4rpx rgba(255, 255, 255, 0.5); /* 顶部微高光,增加拟物质感 */
|
||||
}
|
||||
|
||||
.service-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.service-desc {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8; /* 银灰色 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,13 +4,18 @@
|
||||
<view v-if="!auditMode" class="toolbar">
|
||||
<button class="btn" @click="goForm()">新建录入</button>
|
||||
</view>
|
||||
<view v-for="item in list" :key="item.id" class="card" @click="goDetail(item)">
|
||||
<view class="row">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="status" :class="'s' + item.status">{{ statusText(item.status) }}</text>
|
||||
</view>
|
||||
<view class="sub">{{ item.mobile }} · {{ displayTime(item.created_at) }}</view>
|
||||
</view>
|
||||
<!-- 录入/审核列表卡:统一 xk-list-card 规范(首字头像 + 状态色 + 查看操作) -->
|
||||
<xk-list-card
|
||||
v-for="item in list" :key="item.id"
|
||||
:avatar-text="item.name"
|
||||
:title="item.name"
|
||||
:time="displayTime(item.created_at)"
|
||||
:rows="[item.mobile || '-']"
|
||||
:status="{ text: statusText(item.status), color: statusColor(item.status) }"
|
||||
:actions="[{ key: 'detail', text: auditMode ? '去审核' : '查看详情', type: auditMode ? 'primary' : 'ghost' }]"
|
||||
@action="goDetail(item)"
|
||||
@click="goDetail(item)"
|
||||
></xk-list-card>
|
||||
<view v-if="!list.length" class="empty">暂无记录</view>
|
||||
</view>
|
||||
</business-page-layout>
|
||||
@@ -72,6 +77,10 @@ export default {
|
||||
statusText(s) {
|
||||
return { 0: '待审核', 1: '已通过', 2: '已拒绝' }[Number(s)] || '-'
|
||||
},
|
||||
/** 状态色映射:与 statusText 对应(橙=待审核 / 绿=通过 / 红=拒绝) */
|
||||
statusColor(s) {
|
||||
return { 0: '#FF7D00', 1: '#00B42A', 2: '#F53F3F' }[Number(s)] || '#999'
|
||||
},
|
||||
displayTime(t) {
|
||||
if (t == null || t === '') return '-'
|
||||
if (typeof t === 'string' && (t.includes('-') || t.includes(':'))) {
|
||||
@@ -98,13 +107,5 @@ export default {
|
||||
.page-wrap { padding: 24rpx; }
|
||||
.toolbar { margin-bottom: 24rpx; }
|
||||
.btn { background: #6acdbb; color: #fff; font-size: 28rpx; }
|
||||
.card { background: #fff; padding: 28rpx; border-radius: 16rpx; margin-bottom: 16rpx; }
|
||||
.row { display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { font-size: 30rpx; font-weight: 600; }
|
||||
.status { font-size: 24rpx; }
|
||||
.s0 { color: #FF7D00; }
|
||||
.s1 { color: #00B42A; }
|
||||
.s2 { color: #F53F3F; }
|
||||
.sub { margin-top: 12rpx; color: #86909C; font-size: 24rpx; }
|
||||
.empty { text-align: center; color: #86909C; padding: 80rpx 0; }
|
||||
</style>
|
||||
|
||||
@@ -1,79 +1,38 @@
|
||||
<template>
|
||||
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||
<view class="mine-container">
|
||||
<view class="profile-panel">
|
||||
<view class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ displayName }}</text>
|
||||
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-panel">
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
<view class="logout-wrap">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||
@click="logout"
|
||||
>退出登录</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 页面主体统一收进 admin-my-panel(资料区 + 个人中心入口 + 菜单卡 + 退出) -->
|
||||
<admin-my-panel
|
||||
ref="myPanel"
|
||||
:display-name="displayName"
|
||||
:avatar="avatar"
|
||||
:role-text="roleText"
|
||||
fallback-char="管"
|
||||
/>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 平台管理员「我的」:对齐医生(菜单卡 + 绿色退出),保留绑定微信
|
||||
* 平台管理员「我的」:主体复用 admin-my-panel 共享组件
|
||||
* 本页只保留 layout、businessContext 注入与本地缓存读取
|
||||
*/
|
||||
import BusinessPageLayout from '@/subPackages/sub_platform_admin/components/business-page-layout.vue'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||
import AdminMyPanel from '@/components/admin-my-panel/admin-my-panel.vue'
|
||||
import { getPlatformAdminContext } from '@/subPackages/sub_platform_admin/common/context.js'
|
||||
import { clearLoginStorage } from '@/utils/loginSession.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout, AccountSwitchEntry, WxBindEntry },
|
||||
components: { BusinessPageLayout, AdminMyPanel },
|
||||
provide() { return { businessContext: getPlatformAdminContext() } },
|
||||
data() { return { displayName: '', roleText: '平台管理员' } },
|
||||
computed: {
|
||||
avatarText() { return (this.displayName || '管').slice(0, 1) },
|
||||
data() {
|
||||
return { displayName: '', avatar: '', roleText: '平台管理员' }
|
||||
},
|
||||
onShow() {
|
||||
// 个人中心改完昵称/头像返回时重读缓存立即生效
|
||||
const user = uni.getStorageSync('platform_admin_user') || {}
|
||||
this.displayName = user.nick_name || '管理员'
|
||||
this.avatar = user.avatar || ''
|
||||
this.roleText = (user.roles && user.roles.name) || '平台管理员'
|
||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示', content: '确定退出登录?', confirmColor: '#6ACDBB',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
clearLoginStorage()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
},
|
||||
})
|
||||
},
|
||||
if (this.$refs.myPanel) this.$refs.myPanel.refreshWxBind()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mine-container { min-height: 100vh; background: #F5F7F8; padding-bottom: 60rpx; }
|
||||
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
||||
.avatar-circle {
|
||||
width: 128rpx; height: 128rpx; border-radius: 50%;
|
||||
background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx;
|
||||
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||
}
|
||||
.user-info { margin-left: 32rpx; }
|
||||
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
||||
.role-badge { display: inline-block; background: rgba(106,205,187,.1); padding: 4rpx 16rpx; border-radius: 8rpx; }
|
||||
.role-text { font-size: 22rpx; color: #4db6a8; }
|
||||
.setting-panel { background: #fff; padding: 8rpx 40rpx 16rpx; margin-bottom: 24rpx; }
|
||||
.logout-wrap { padding: 0 40rpx 32rpx; }
|
||||
</style>
|
||||
|
||||
@@ -11,29 +11,31 @@
|
||||
<view v-if="loading" class="empty">加载中...</view>
|
||||
<view v-else-if="!list.length" class="empty">暂无门店</view>
|
||||
|
||||
<view
|
||||
<!-- 门店列表卡:统一 xk-list-card 规范(类型标签 + 地址/联系信息 + 权益标签 + 管理操作) -->
|
||||
<xk-list-card
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="card"
|
||||
hover-class="card-hover"
|
||||
:avatar-text="item.name"
|
||||
:title="item.name"
|
||||
:tags="[{ text: clinicTypeText(item.clinic_type), type: 'primary' }]"
|
||||
:actions="[{ key: 'detail', text: '管理门店', type: 'primary' }]"
|
||||
@action="goDetail(item)"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<view class="card-head">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="type-tag">{{ clinicTypeText(item.clinic_type) }}</text>
|
||||
</view>
|
||||
<text class="addr">{{ item.position || '暂无地址' }}</text>
|
||||
<view class="meta-row">
|
||||
<text>ID {{ item.id }}</text>
|
||||
<text v-if="item.mobile">电话 {{ item.mobile }}</text>
|
||||
</view>
|
||||
<view class="tag-row">
|
||||
<text class="mini-tag" :class="{ on: Number(item.salesperson_see_price) === 1 }">推广员可见价格</text>
|
||||
<text class="mini-tag" :class="{ on: Number(item.is_shipping_free) === 1 }">包邮</text>
|
||||
<text v-if="Number(item.bank_card_report_status) === 0" class="mini-tag warn">需报备银行卡</text>
|
||||
<text v-else-if="item.bank_card_report_status_text" class="mini-tag bank">{{ item.bank_card_report_status_text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<template #body>
|
||||
<text class="addr">{{ item.position || '暂无地址' }}</text>
|
||||
<view class="meta-row">
|
||||
<text>ID {{ item.id }}</text>
|
||||
<text v-if="item.mobile">电话 {{ item.mobile }}</text>
|
||||
</view>
|
||||
<view class="tag-row">
|
||||
<text class="mini-tag" :class="{ on: Number(item.salesperson_see_price) === 1 }">推广员可见价格</text>
|
||||
<text class="mini-tag" :class="{ on: Number(item.is_shipping_free) === 1 }">包邮</text>
|
||||
<text v-if="Number(item.bank_card_report_status) === 0" class="mini-tag warn">需报备银行卡</text>
|
||||
<text v-else-if="item.bank_card_report_status_text" class="mini-tag bank">{{ item.bank_card_report_status_text }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</xk-list-card>
|
||||
</view>
|
||||
</store-page-layout>
|
||||
</template>
|
||||
@@ -110,14 +112,7 @@ export default {
|
||||
background: #6ACDBB; color: #fff; padding: 16rpx 32rpx; border-radius: 12rpx; font-size: 26rpx;
|
||||
}
|
||||
.empty { text-align: center; color: #999; padding: 80rpx 0; font-size: 28rpx; }
|
||||
.card {
|
||||
background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx;
|
||||
}
|
||||
.card-hover { opacity: 0.92; }
|
||||
.card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
|
||||
.name { font-size: 30rpx; font-weight: 600; color: #1f2937; }
|
||||
.type-tag { font-size: 22rpx; color: #6ACDBB; background: #f0fbf8; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
||||
.addr { display: block; font-size: 24rpx; color: #6b7280; margin-bottom: 12rpx; }
|
||||
.addr { display: block; font-size: 24rpx; color: #6b7280; margin: 8rpx 0 12rpx; }
|
||||
.meta-row { display: flex; gap: 24rpx; font-size: 22rpx; color: #9ca3af; margin-bottom: 12rpx; }
|
||||
.tag-row { display: flex; flex-wrap: wrap; gap: 8rpx; }
|
||||
.mini-tag {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getPlatformAdminContext } from '@/subPackages/sub_business_shared/commo
|
||||
|
||||
const BUSINESS_PACKAGE_ROOT = '/subPackages/sub_business_shared';
|
||||
const SALESPERSON_HOME = '/subPackages/sub_salesperson/home/index';
|
||||
const SALESPERSON_FEATURES = '/subPackages/sub_salesperson/features/index';
|
||||
const SALESPERSON_MY = '/subPackages/sub_salesperson/my/index';
|
||||
|
||||
export function getSalespersonContext() {
|
||||
@@ -24,6 +25,7 @@ export function getSalespersonContext() {
|
||||
},
|
||||
tabList: [
|
||||
{ text: '工作台', iconPath: 'home', selectedIconPath: 'home-fill', pagePath: SALESPERSON_HOME },
|
||||
{ text: '功能', iconPath: 'grid', selectedIconPath: 'grid-fill', pagePath: SALESPERSON_FEATURES },
|
||||
{ text: '我的', iconPath: 'account', selectedIconPath: 'account-fill', pagePath: SALESPERSON_MY },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
<slot />
|
||||
</view>
|
||||
<image-gallery-picker />
|
||||
<!-- 高度显式传 rpx(100 与 body 预留一致):默认 50px 固定像素在宽屏机型会与内部 rpx 定位的 icon/文字冲突挤压 -->
|
||||
<u-tabbar
|
||||
v-if="showTabbar"
|
||||
:value="tabIndex"
|
||||
:list="tabList"
|
||||
:height="100"
|
||||
active-color="#6ACDBB"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
<view class="clinic-body" :style="bodyStyle">
|
||||
<slot />
|
||||
</view>
|
||||
<!-- 高度显式传 rpx(100 与 body 预留一致):默认 50px 固定像素在宽屏机型会与内部 rpx 定位的 icon/文字冲突挤压 -->
|
||||
<u-tabbar
|
||||
v-if="showTabbar"
|
||||
:value="tabIndex"
|
||||
:list="tabList"
|
||||
:height="100"
|
||||
active-color="#6ACDBB"
|
||||
@change="onTabChange"
|
||||
/>
|
||||
|
||||
39
subPackages/sub_salesperson/features/index.vue
Normal file
39
subPackages/sub_salesperson/features/index.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<business-page-layout title="功能" :show-tabbar="true" :tab-index="1">
|
||||
<workbench-features ref="features" :api="featuresApi"></workbench-features>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 业务员端「功能」tab 薄壳页
|
||||
* 壳页只负责:套本端 layout(navbar/tabbar 配置)+ 注入本端角色接口,
|
||||
* 搜索/分组滑动/长按收藏/使用上报等全部逻辑在主包共享组件 workbench-features(四端复用)
|
||||
*/
|
||||
import BusinessPageLayout from '@/subPackages/sub_salesperson/components/business-page-layout.vue'
|
||||
import {
|
||||
getSalespersonWorkbenchEntries,
|
||||
getSalespersonEntryUserData,
|
||||
toggleSalespersonFavoriteEntry,
|
||||
reportSalespersonEntryUsage,
|
||||
} from '@/api/salesperson.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout },
|
||||
data() {
|
||||
return {
|
||||
/** 注入共享组件的本端接口集合(统一返回 { res, data, ok }) */
|
||||
featuresApi: {
|
||||
getEntries: getSalespersonWorkbenchEntries,
|
||||
getUserData: getSalespersonEntryUserData,
|
||||
toggleFavorite: toggleSalespersonFavoriteEntry,
|
||||
reportUsage: reportSalespersonEntryUsage,
|
||||
},
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
// 从功能跳转的页面返回时刷新常用/最近使用(首屏加载由组件自身 mounted 完成,此时 ref 还不存在)
|
||||
if (this.$refs.features) this.$refs.features.refreshUserData()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -35,31 +35,15 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 多彩微拟物双列卡片区 -->
|
||||
<!-- 主体内容区:个人功能区(最近使用 / 我的常用 / 更多功能,全部入口收敛到功能 tab) -->
|
||||
<view class="content-wrapper">
|
||||
<view v-for="section in sections" :key="section.key" class="section-panel">
|
||||
<view class="panel-header">
|
||||
<text class="panel-title">{{ section.title }}</text>
|
||||
</view>
|
||||
|
||||
<view class="service-grid">
|
||||
<view
|
||||
v-for="item in section.items"
|
||||
:key="item.key"
|
||||
class="service-card"
|
||||
hover-class="service-card-hover"
|
||||
@click="go(item)"
|
||||
>
|
||||
<view class="service-icon-box" :style="{ background: item.theme.bg }">
|
||||
<u-icon :name="item.icon" size="38" :color="item.theme.color"></u-icon>
|
||||
</view>
|
||||
<view class="service-info">
|
||||
<text class="service-name">{{ item.label }}</text>
|
||||
<text class="service-desc">{{ item.desc || '快捷进入' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<workbench-user-entries
|
||||
:recents="recentItems"
|
||||
:favorites="favItems"
|
||||
:card-long="cardLong"
|
||||
@item-click="onEntryTap"
|
||||
@more="goFeatures"
|
||||
></workbench-user-entries>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
@@ -67,10 +51,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// ... (脚本逻辑保持不变)
|
||||
import BusinessPageLayout from '@/subPackages/sub_salesperson/components/business-page-layout.vue'
|
||||
import { getSalespersonInputStats } from '@/api/salesperson.js'
|
||||
import { getSalespersonInputStats, getSalespersonEntryUserData, reportSalespersonEntryUsage } from '@/api/salesperson.js'
|
||||
import { navigateToInputForm } from '@/subPackages/sub_salesperson/common/inputNavigation.js'
|
||||
import { normalizeEntry } from '@/utils/workbenchEntries.js'
|
||||
import { getCardStyle } from '@/utils/workbenchCardPref.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout },
|
||||
@@ -78,34 +63,71 @@ export default {
|
||||
return {
|
||||
displayName: '',
|
||||
stats: { storeCount: 0, storeApprovedCount: 0, doctorCount: 0, doctorApprovedCount: 0 },
|
||||
themes: {
|
||||
store: { bg: 'linear-gradient(135deg, #ECFEFF, #CFFAFE)', color: '#06B6D4' },
|
||||
doctor: { bg: 'linear-gradient(135deg, #FFFBEB, #FEF3C7)', color: '#D97706' }
|
||||
}
|
||||
/** 我的常用/最近使用原始入口(归一化后存储,表单预填装饰在 computed 注入) */
|
||||
favRaw: [],
|
||||
recentRaw: [],
|
||||
/** 卡片形态偏好:true 长卡一行两个 / false 金刚区宫格(我的页可切换,存本地) */
|
||||
cardLong: false,
|
||||
}
|
||||
},
|
||||
// ... (computed 及 methods 保持原样)
|
||||
computed: {
|
||||
sections() {
|
||||
return [
|
||||
{ key: 'input', title: '信息录入', items: [
|
||||
{ key: 'store-form', label: '门店录入', desc: '新增线下门店', path: '/subPackages/sub_salesperson/store-input/form', inputType: 'store', isForm: true, icon: 'plus-square-fill', theme: this.themes.store },
|
||||
{ key: 'doctor-form', label: '医生录入', desc: '新增合作医生', path: '/subPackages/sub_salesperson/doctor-input/form', inputType: 'doctor', isForm: true, icon: 'plus-circle-fill', theme: this.themes.doctor },
|
||||
]},
|
||||
{ key: 'list', title: '档案管理', items: [
|
||||
{ key: 'my-store', label: '我的诊所', desc: '查看所属正式诊所', path: '/subPackages/sub_platform_admin/store/index', icon: 'home', theme: this.themes.store },
|
||||
{ key: 'store-list', label: '门店列表', desc: '查看已录门店', path: '/subPackages/sub_salesperson/store-input/index', icon: 'home-fill', theme: this.themes.store },
|
||||
{ key: 'doctor-list', label: '医生列表', desc: '查看已录医生', path: '/subPackages/sub_salesperson/doctor-input/index', icon: 'account-fill', theme: this.themes.doctor },
|
||||
]}
|
||||
]
|
||||
}
|
||||
/** 我的常用(录入表单预填标记在这里注入) */
|
||||
favItems() {
|
||||
return this.favRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
/** 最近使用 */
|
||||
recentItems() {
|
||||
return this.recentRaw.map((item) => this.decorateItem(item))
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
const user = uni.getStorageSync('salesperson_user') || {}
|
||||
this.displayName = user.nick_name || '业务员'
|
||||
// 卡片形态偏好每次 onShow 重读:我的页切换后返回立即生效
|
||||
this.cardLong = getCardStyle() === 'long'
|
||||
this.fetchDashboardData()
|
||||
this.loadUserEntries()
|
||||
},
|
||||
methods: {
|
||||
/** 拉取我的常用 + 最近使用:失败静默留空(首页有引导条 + 更多功能卡兜底) */
|
||||
async loadUserEntries() {
|
||||
try {
|
||||
const wrap = await getSalespersonEntryUserData()
|
||||
if (wrap && wrap.ok && wrap.data) {
|
||||
this.favRaw = this.normalizeList(wrap.data.favorites)
|
||||
this.recentRaw = this.normalizeList(wrap.data.recents)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('常用/最近使用加载失败', e)
|
||||
}
|
||||
},
|
||||
/** 后端入口对象数组 → 渲染项数组(与功能页同一映射) */
|
||||
normalizeList(list) {
|
||||
if (!Array.isArray(list)) return []
|
||||
const items = []
|
||||
list.forEach((entry) => {
|
||||
const item = normalizeEntry(entry)
|
||||
if (item) items.push(item)
|
||||
})
|
||||
return items
|
||||
},
|
||||
/** 按 code 注入本端行为:录入表单预填标记 */
|
||||
decorateItem(item) {
|
||||
const formMap = { sp_store_form: 'store', sp_doctor_form: 'doctor' }
|
||||
if (formMap[item.key]) {
|
||||
return { ...item, isForm: true, inputType: formMap[item.key] }
|
||||
}
|
||||
return item
|
||||
},
|
||||
/** 入口点击:静默上报使用(保持最近使用新鲜)→ 表单预填/普通跳转 */
|
||||
onEntryTap(item) {
|
||||
reportSalespersonEntryUsage(item.key).catch(() => {})
|
||||
this.go(item)
|
||||
},
|
||||
/** 更多功能/空态引导:切到功能 tab(tabbar 页统一走 reLaunch) */
|
||||
goFeatures() {
|
||||
uni.reLaunch({ url: '/subPackages/sub_salesperson/features/index' })
|
||||
},
|
||||
async fetchDashboardData() {
|
||||
try {
|
||||
const wrap = await getSalespersonInputStats()
|
||||
@@ -141,25 +163,16 @@ export default {
|
||||
.store-name { font-size: 42rpx; font-weight: 600; color: #fff; }
|
||||
.store-sub { font-size: 26rpx; color: rgba(255, 255, 255, 0.85); }
|
||||
|
||||
/* 看板区域优化 */
|
||||
/* 看板区域:与各端工作台看板统一规格(24rpx 圆角 + 轻阴影 + DIN 数字) */
|
||||
.dashboard-wrapper { margin: -40rpx 30rpx 30rpx; }
|
||||
.dashboard-card { background: #fff; border-radius: 32rpx; padding: 40rpx 30rpx; display: grid; grid-template-columns: repeat(2, 1fr); gap: 20rpx; box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05); }
|
||||
.dashboard-card { background: #fff; border-radius: 24rpx; padding: 40rpx 30rpx; display: grid; grid-template-columns: repeat(2, 1fr); gap: 20rpx; box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03); }
|
||||
.stat-item { display: flex; flex-direction: column; align-items: center; }
|
||||
.stat-label { font-size: 22rpx; color: #94A3B8; margin-bottom: 12rpx; }
|
||||
.stat-value-group { display: flex; align-items: baseline; }
|
||||
.stat-value { font-size: 42rpx; font-weight: 700; color: #1E293B; }
|
||||
.stat-value { font-size: 42rpx; font-weight: 700; color: #1E293B; font-family: "DIN Alternate", sans-serif; }
|
||||
.stat-slash { font-size: 32rpx; color: #CBD5E1; margin: 0 8rpx; }
|
||||
.stat-value-highlight { font-size: 42rpx; font-weight: 700; color: #059669; }
|
||||
.stat-value-highlight { font-size: 42rpx; font-weight: 700; color: #059669; font-family: "DIN Alternate", sans-serif; }
|
||||
|
||||
/* 业务卡片区保持原样 */
|
||||
/* 个人功能区(分区/宫格样式在 workbench-user-entries 组件内) */
|
||||
.content-wrapper { position: relative; z-index: 10; }
|
||||
.section-panel { margin: 0 30rpx 30rpx; }
|
||||
.panel-title { font-size: 34rpx; font-weight: 700; color: #1E293B; margin-bottom: 24rpx; display: block; padding-left: 10rpx;}
|
||||
.service-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20rpx; }
|
||||
.service-card { background: #FFFFFF; border-radius: 28rpx; padding: 30rpx 24rpx; display: flex; align-items: center; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02); }
|
||||
.service-card-hover { transform: translateY(-2rpx); }
|
||||
.service-icon-box { width: 76rpx; height: 76rpx; border-radius: 22rpx; display: flex; align-items: center; justify-content: center; margin-right: 20rpx; flex-shrink: 0; box-shadow: inset 0 2rpx 4rpx rgba(255, 255, 255, 0.5); }
|
||||
.service-info { display: flex; flex-direction: column; flex: 1; overflow: hidden; }
|
||||
.service-name { font-size: 28rpx; font-weight: 600; color: #1E293B; line-height: 1.2; }
|
||||
.service-desc { font-size: 20rpx; color: #94A3B8; margin-top: 4rpx; line-height: 1.2; }
|
||||
</style>
|
||||
@@ -6,18 +6,18 @@
|
||||
:input-type="inputType"
|
||||
@select="onDraftSelect"
|
||||
/>
|
||||
<view v-for="item in list" :key="item.id" class="card">
|
||||
<view class="row" @click="goDetail(item)">
|
||||
<text class="name">{{ item.name }}</text>
|
||||
<text class="status" :class="'s' + item.status">{{ statusText(item.status) }}</text>
|
||||
</view>
|
||||
<view class="sub" @click="goDetail(item)">{{ item.mobile }} · {{ displayTime(item.created_at) }}</view>
|
||||
<view class="card-actions" @click.stop>
|
||||
<button class="mini-btn outline" @click="goDetail(item)">查看详情</button>
|
||||
<button v-if="canEditItem(item)" class="mini-btn outline" @click="goEdit(item)">继续编辑</button>
|
||||
<button v-if="showDoctorEntry" class="mini-btn" @click="goDoctorInput(item)">录入医生</button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 录入列表卡:统一 xk-list-card 规范(首字头像 + 状态色 + 主/次操作按钮) -->
|
||||
<xk-list-card
|
||||
v-for="item in list" :key="item.id"
|
||||
:avatar-text="item.name"
|
||||
:title="item.name"
|
||||
:time="displayTime(item.created_at)"
|
||||
:rows="[item.mobile || '-']"
|
||||
:status="{ text: statusText(item.status), color: statusColor(item.status) }"
|
||||
:actions="cardActions(item)"
|
||||
@action="onCardAction($event, item)"
|
||||
@click="goDetail(item)"
|
||||
></xk-list-card>
|
||||
<view v-if="!list.length" class="empty">暂无记录</view>
|
||||
</view>
|
||||
<view v-if="showListFooter" class="list-footer">
|
||||
@@ -100,6 +100,23 @@ export default {
|
||||
statusText(s) {
|
||||
return { 0: '待审核', 1: '已通过', 2: '已拒绝' }[Number(s)] || '-'
|
||||
},
|
||||
/** 状态色映射:与 statusText 对应(橙=待审核 / 绿=通过 / 红=拒绝) */
|
||||
statusColor(s) {
|
||||
return { 0: '#FF7D00', 1: '#00B42A', 2: '#F53F3F' }[Number(s)] || '#999'
|
||||
},
|
||||
/** 组装卡片操作按钮:主操作放最后(录入医生),次操作用浅底描边 */
|
||||
cardActions(item) {
|
||||
const actions = [{ key: 'detail', text: '查看详情', type: 'ghost' }]
|
||||
if (this.canEditItem(item)) actions.push({ key: 'edit', text: '继续编辑', type: 'ghost' })
|
||||
if (this.showDoctorEntry) actions.push({ key: 'doctor', text: '录入医生', type: 'primary' })
|
||||
return actions
|
||||
},
|
||||
/** 卡片操作分发:按 key 路由到对应跳转 */
|
||||
onCardAction(act, item) {
|
||||
if (act.key === 'edit') return this.goEdit(item)
|
||||
if (act.key === 'doctor') return this.goDoctorInput(item)
|
||||
this.goDetail(item)
|
||||
},
|
||||
displayTime(t) {
|
||||
if (t == null || t === '') return '-'
|
||||
if (typeof t === 'string' && (t.includes('-') || t.includes(':'))) {
|
||||
@@ -156,16 +173,5 @@ export default {
|
||||
padding: 22rpx;
|
||||
margin: 0;
|
||||
}
|
||||
.card { background: #fff; padding: 28rpx; border-radius: 16rpx; margin-bottom: 16rpx; }
|
||||
.row { display: flex; justify-content: space-between; align-items: center; }
|
||||
.name { font-size: 30rpx; font-weight: 600; }
|
||||
.status { font-size: 24rpx; }
|
||||
.s0 { color: #FF7D00; }
|
||||
.s1 { color: #00B42A; }
|
||||
.s2 { color: #F53F3F; }
|
||||
.sub { margin-top: 12rpx; color: #86909C; font-size: 24rpx; }
|
||||
.card-actions { margin-top: 16rpx; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 12rpx; }
|
||||
.mini-btn { font-size: 24rpx; padding: 8rpx 24rpx; background: #6acdbb; color: #fff; line-height: 1.6; margin: 0; }
|
||||
.mini-btn.outline { background: #fff; color: #6acdbb; border: 1rpx solid #6acdbb; }
|
||||
.empty { text-align: center; color: #86909C; padding: 80rpx 0; }
|
||||
</style>
|
||||
|
||||
@@ -1,77 +1,38 @@
|
||||
<template>
|
||||
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||
<view class="mine-container">
|
||||
<view class="profile-panel">
|
||||
<view class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{ displayName }}</text>
|
||||
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="setting-panel">
|
||||
<wx-bind-entry ref="wxBindEntry" />
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
<view class="logout-wrap">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||
@click="logout"
|
||||
>退出登录</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 页面主体统一收进 admin-my-panel(资料区 + 个人中心入口 + 菜单卡 + 退出) -->
|
||||
<admin-my-panel
|
||||
ref="myPanel"
|
||||
:display-name="displayName"
|
||||
:avatar="avatar"
|
||||
:role-text="roleText"
|
||||
fallback-char="业"
|
||||
/>
|
||||
</business-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 业务员「我的」:对齐医生(菜单卡 + 绿色退出),保留绑定微信
|
||||
* 业务员「我的」:主体复用 admin-my-panel 共享组件
|
||||
* 本页只保留 layout、businessContext 注入与本地缓存读取
|
||||
*/
|
||||
import BusinessPageLayout from '@/subPackages/sub_salesperson/components/business-page-layout.vue'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||
import AdminMyPanel from '@/components/admin-my-panel/admin-my-panel.vue'
|
||||
import { getSalespersonContext } from '@/subPackages/sub_salesperson/common/context.js'
|
||||
import { clearLoginStorage } from '@/utils/loginSession.js'
|
||||
|
||||
export default {
|
||||
components: { BusinessPageLayout, AccountSwitchEntry, WxBindEntry },
|
||||
components: { BusinessPageLayout, AdminMyPanel },
|
||||
provide() { return { businessContext: getSalespersonContext() } },
|
||||
data() { return { displayName: '', roleText: '业务员' } },
|
||||
computed: { avatarText() { return (this.displayName || '业').slice(0, 1) } },
|
||||
data() {
|
||||
return { displayName: '', avatar: '', roleText: '业务员' }
|
||||
},
|
||||
onShow() {
|
||||
// 个人中心改完昵称/头像返回时重读缓存立即生效
|
||||
const user = uni.getStorageSync('salesperson_user') || {}
|
||||
this.displayName = user.nick_name || '业务员'
|
||||
this.avatar = user.avatar || ''
|
||||
this.roleText = (user.roles && user.roles.name) || '业务员'
|
||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
uni.showModal({
|
||||
title: '提示', content: '确定退出登录?', confirmColor: '#6ACDBB',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
clearLoginStorage()
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
},
|
||||
})
|
||||
},
|
||||
if (this.$refs.myPanel) this.$refs.myPanel.refreshWxBind()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mine-container { min-height: 100vh; background: #F5F7F8; padding-bottom: 60rpx; }
|
||||
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
||||
.avatar-circle {
|
||||
width: 128rpx; height: 128rpx; border-radius: 50%;
|
||||
background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx;
|
||||
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||
}
|
||||
.user-info { margin-left: 32rpx; }
|
||||
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
||||
.role-badge { display: inline-block; background: rgba(106,205,187,.1); padding: 4rpx 16rpx; border-radius: 8rpx; }
|
||||
.role-text { font-size: 22rpx; color: #4db6a8; }
|
||||
.setting-panel { background: #fff; padding: 8rpx 40rpx 16rpx; margin-bottom: 24rpx; }
|
||||
.logout-wrap { padding: 0 40rpx 32rpx; }
|
||||
</style>
|
||||
|
||||
@@ -20,42 +20,46 @@
|
||||
<view v-if="loading" class="loading-tip">加载中...</view>
|
||||
<view v-else-if="!list.length" class="empty">暂无推广员</view>
|
||||
|
||||
<!-- 推广员卡:信息分层(姓名/门店/分成标签)+ 三列统计带 + 主/次操作胶囊按钮 -->
|
||||
<view v-for="item in list" :key="item.id" class="card">
|
||||
<view class="card-head">
|
||||
<view>
|
||||
<text class="name">{{ item.nick_name || '未知' }}</text>
|
||||
<text v-if="item.store_name" class="store">{{ item.store_name }}</text>
|
||||
<view class="head-left">
|
||||
<view class="avatar-text">{{ (item.nick_name || '未')[0] }}</view>
|
||||
<view class="head-info">
|
||||
<text class="name">{{ item.nick_name || '未知' }}</text>
|
||||
<text class="store">{{ item.store_name || item.phone }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tags">
|
||||
<text class="tag">{{ splitLabel(item.split_type) }}</text>
|
||||
<text v-if="Number(item.tcm_split) > 0" class="tag tcm">中药 {{ item.tcm_split }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<text class="phone">{{ item.phone }}</text>
|
||||
<view class="stat-row">
|
||||
<text>累计获客</text>
|
||||
<text class="num">{{ (item.count && item.count.user_number) || 0 }}</text>
|
||||
<!-- 统计带:三列并排,数值突出(对齐工作台看板视觉) -->
|
||||
<view class="stat-strip">
|
||||
<view class="stat-cell">
|
||||
<text class="stat-num">{{ (item.count && item.count.user_number) || 0 }}</text>
|
||||
<text class="stat-label">累计获客</text>
|
||||
</view>
|
||||
<view class="stat-row">
|
||||
<text>待结算</text>
|
||||
<text class="num pending">¥{{ (item.count && item.count.pending_amount) || '0.00' }}</text>
|
||||
<view class="stat-cell">
|
||||
<text class="stat-num pending">¥{{ (item.count && item.count.pending_amount) || '0.00' }}</text>
|
||||
<text class="stat-label">待结算</text>
|
||||
</view>
|
||||
<view class="stat-row">
|
||||
<text>后台账号</text>
|
||||
<text :class="item.has_admin_account ? 'ok' : 'warn'">{{ item.has_admin_account ? '已开通' : '未开通' }}</text>
|
||||
<view class="stat-cell">
|
||||
<text class="stat-num" :class="item.has_admin_account ? 'ok' : 'warn'">{{ item.has_admin_account ? '已开通' : '未开通' }}</text>
|
||||
<text class="stat-label">后台账号</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="actions">
|
||||
<view class="action-btn" @click="goEdit(item)">编辑</view>
|
||||
<view class="action-btn primary" @click="goCommission(item)">服务费与结算</view>
|
||||
<view v-if="item.qr_code && item.qr_code.qr_code" class="action-btn" @click="openQr(item)">二维码</view>
|
||||
<view class="action-btn" @click="goLeads(item)">获客</view>
|
||||
<view class="action-btn ghost" @click="goEdit(item)">编辑</view>
|
||||
<view v-if="item.qr_code && item.qr_code.qr_code" class="action-btn ghost" @click="openQr(item)">二维码</view>
|
||||
<view class="action-btn ghost" @click="goLeads(item)">获客</view>
|
||||
<view
|
||||
v-if="!item.has_admin_account"
|
||||
class="action-btn warn"
|
||||
@click="openAdminAccount(item)"
|
||||
>开通后台</view>
|
||||
<view class="action-btn primary" @click="goCommission(item)">服务费与结算</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -200,27 +204,47 @@ export default {
|
||||
text-align: center; padding: 20rpx; border-radius: 16rpx; font-size: 28rpx;
|
||||
}
|
||||
.loading-tip, .empty { text-align: center; color: #999; padding: 80rpx 0; }
|
||||
/* 卡片容器:与 xk-list-card 同规格(24rpx 圆角 + 轻阴影) */
|
||||
.card {
|
||||
background: #fff; border-radius: 20rpx; padding: 28rpx; margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.04);
|
||||
background: #fff; border-radius: 24rpx; padding: 28rpx 24rpx; margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
.card-head { display: flex; justify-content: space-between; margin-bottom: 16rpx; }
|
||||
.name { display: block; font-size: 32rpx; font-weight: 600; }
|
||||
.store { font-size: 24rpx; color: #999; }
|
||||
.tags { display: flex; flex-direction: column; gap: 8rpx; align-items: flex-end; }
|
||||
.tag { font-size: 22rpx; background: #EFF6FF; color: #3B82F6; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
||||
.card-head { display: flex; justify-content: space-between; align-items: flex-start; }
|
||||
.head-left { display: flex; align-items: center; min-width: 0; }
|
||||
/* 首字头像:青绿浅底,与 xk-list-card 兜底头像一致 */
|
||||
.avatar-text {
|
||||
width: 80rpx; height: 80rpx; border-radius: 50%; flex-shrink: 0;
|
||||
background: #e8f7f3; color: #4db6a8; font-size: 32rpx; font-weight: 600;
|
||||
display: flex; align-items: center; justify-content: center; margin-right: 20rpx;
|
||||
}
|
||||
.head-info { display: flex; flex-direction: column; min-width: 0; }
|
||||
.name { font-size: 32rpx; font-weight: 500; color: #333; }
|
||||
.store { font-size: 24rpx; color: #999; margin-top: 4rpx; }
|
||||
.tags { display: flex; flex-direction: column; gap: 8rpx; align-items: flex-end; flex-shrink: 0; }
|
||||
.tag { font-size: 22rpx; background: #EFF6FF; color: #3B82F6; padding: 4rpx 12rpx; border-radius: 20rpx; }
|
||||
.tag.tcm { background: #F0FDF4; color: #22C55E; }
|
||||
.phone { font-size: 26rpx; color: #666; display: block; margin-bottom: 12rpx; }
|
||||
.stat-row { display: flex; justify-content: space-between; font-size: 26rpx; margin-bottom: 8rpx; }
|
||||
.num { font-weight: 600; }
|
||||
/* 统计带:浅灰底圆角内嵌,三列均分,数值加粗突出 */
|
||||
.stat-strip {
|
||||
display: flex; background: #f8fafb; border-radius: 16rpx;
|
||||
padding: 20rpx 0; margin-top: 20rpx;
|
||||
}
|
||||
.stat-cell {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center;
|
||||
border-right: 1rpx solid #eef1f3;
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
.stat-num { font-size: 30rpx; font-weight: 600; color: #333; }
|
||||
.stat-label { font-size: 22rpx; color: #94A3B8; margin-top: 6rpx; }
|
||||
.pending { color: #F97316; }
|
||||
.ok { color: #22C55E; }
|
||||
.warn { color: #EF4444; }
|
||||
.actions { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 20rpx; }
|
||||
/* 操作区:胶囊按钮,主操作实心青绿、次操作浅底描边、警示操作琥珀 */
|
||||
.actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 16rpx; margin-top: 20rpx; }
|
||||
.action-btn {
|
||||
font-size: 24rpx; padding: 10rpx 20rpx; border-radius: 10rpx;
|
||||
background: #f5f5f5; color: #333;
|
||||
font-size: 24rpx; padding: 10rpx 28rpx; border-radius: 30rpx;
|
||||
font-weight: 500; line-height: 1.4; text-align: center;
|
||||
}
|
||||
.action-btn.ghost { color: #6ACDBB; background: #f0f9f7; border: 1rpx solid #c2ece2; }
|
||||
.action-btn.primary { background: #6ACDBB; color: #fff; }
|
||||
.action-btn.warn { background: #FEF3C7; color: #D97706; }
|
||||
.action-btn.warn { background: #FEF3C7; color: #D97706; border: 1rpx solid #fde68a; }
|
||||
</style>
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
<template>
|
||||
<view class="box">
|
||||
<view class="b-r-8 white p-32 flex-row flex-jus-sp flex-ali-center m-b-2">
|
||||
<text class="left">停诊公告</text>
|
||||
<u-switch v-model="form.checked" @change="disabled=form.id?false:!form.checked"></u-switch>
|
||||
</view>
|
||||
<view class="b-r-8 white p-32 " style="margin-bottom: calc(env(safe-area-inset-bottom) + 110rpx);"
|
||||
v-show="form.checked">
|
||||
<u-form :model="form" ref="announcement" label-width="150rpx">
|
||||
<u-form-item label="公告内容" prop="msg" label-position="top">
|
||||
<view class="flex-col bg b-r-8 p-row-16" style="width: 646rpx;">
|
||||
<u-input v-model="form.msg" placeholder="请输入" type="textarea" :maxlength="500" auto-height />
|
||||
<d-text :text="`${form['msg']?form['msg'].length:0}/500`" color="#C4C7CC" width="614"
|
||||
align="right">
|
||||
</d-text>
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item label="开始时间" prop="startTime">
|
||||
<u-input input-align="right" v-model="form.startTime" type="select" :select-open="select1"
|
||||
@click="select1=true;show=true" placeholder="请选择" />
|
||||
</u-form-item>
|
||||
<u-form-item label="结束时间" prop="endTime" :border-bottom="false">
|
||||
<u-input input-align="right" v-model="form.endTime" type="select" :select-open="select2"
|
||||
@click="select2=true;show=true" placeholder="请选择" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class="button">
|
||||
<u-button :throttle-time="0" type="primary" shape="circle" @click="submit" :ripple="true"
|
||||
:disabled="form.id&&!form.checked?false:!form.checked">确定</u-button>
|
||||
</view>
|
||||
<u-picker :safe-area-inset-bottom="true" :mask-close-able="false" @cancel="select1 = false;select2 = false"
|
||||
v-model="show" mode="time" :params="params" @confirm="confirm"></u-picker>
|
||||
<d-loading-page :loading="loading" :loadingText="loadingText"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
queryNotice,
|
||||
saveClose
|
||||
} from '@/api/all.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
disabled: true,
|
||||
loadingText: '正在加载中...',
|
||||
info: '',
|
||||
loading: true,
|
||||
form: {},
|
||||
show: false,
|
||||
params: {
|
||||
year: true,
|
||||
month: true,
|
||||
day: true,
|
||||
hour: true,
|
||||
minute: true,
|
||||
second: true,
|
||||
timestamp: true
|
||||
},
|
||||
select1: 0,
|
||||
select2: 0,
|
||||
rules: {
|
||||
msg: [{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (!this.form['msg']) {
|
||||
callback(new Error('公告内容不能为空'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: ['change', 'blur']
|
||||
}],
|
||||
startTime: [{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (!this.form['startTime']) {
|
||||
callback(new Error('请选择开始时间'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: ['change', 'blur']
|
||||
}],
|
||||
endTime: [{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (!this.form['endTime']) {
|
||||
callback(new Error('请选择结束时间'));
|
||||
return
|
||||
}
|
||||
if (this.endTimestamp <= this.startTimestamp) {
|
||||
callback(new Error('结束时间不能小于等于开始时间'));
|
||||
return
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: ['change', 'blur']
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.announcement.setRules(this.rules);
|
||||
})
|
||||
this.queryNotice()
|
||||
},
|
||||
methods: {
|
||||
queryNotice() {
|
||||
queryNotice().then(res => {
|
||||
if (res.errcode == 0) {
|
||||
if (res.data.close_notice) {
|
||||
this.form = {
|
||||
checked: res.data.close_notice ? true : false,
|
||||
msg: res.data.content,
|
||||
startTime: this.$u.timeFormat(res.data.start_time, 'yyyy-mm-dd hh:MM:ss'),
|
||||
endTime: this.$u.timeFormat(res.data.end_time, 'yyyy-mm-dd hh:MM:ss'),
|
||||
}
|
||||
this.startTimestamp = res.data.start_time
|
||||
this.endTimestamp = res.data.end_time
|
||||
}
|
||||
this.form.id = res.data.id
|
||||
this.loading = false
|
||||
this.disabled = false
|
||||
} else {
|
||||
this.disabled = true
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
saveClose(data) {
|
||||
saveClose(data).then(res => {
|
||||
if (res.errcode == 0) {
|
||||
this.$toast('提交成功')
|
||||
this.loading = false
|
||||
} else {
|
||||
this.$toast(res.msg)
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
confirm(e) {
|
||||
let time = `${e.month}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`
|
||||
if (this.select1) {
|
||||
this.form.startTime = this.$u.timeFormat(e.timestamp, 'yyyy-mm-dd hh:MM:ss')
|
||||
this.startTimestamp = e.timestamp
|
||||
this.select1 = false
|
||||
}
|
||||
if (this.select2) {
|
||||
this.form.endTime = this.$u.timeFormat(e.timestamp, 'yyyy-mm-dd hh:MM:ss')
|
||||
this.endTimestamp = e.timestamp
|
||||
this.select2 = false
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
this.$refs.announcement.validate(valid => {
|
||||
if (valid) {
|
||||
let y = new Date().getFullYear()
|
||||
const data = {
|
||||
close_notice: this.form.checked ? 1 : 0,
|
||||
content: this.form.msg,
|
||||
start_time: this.startTimestamp,
|
||||
end_time: this.endTimestamp,
|
||||
}
|
||||
this.loadingText = '请稍后...'
|
||||
this.loading = true
|
||||
this.saveClose(data)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
.box {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: fixed;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
height: 110rpx;
|
||||
background: #FFFFFF;
|
||||
border-top: 2rpx solid #F2F3F5;
|
||||
padding: 12rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<view class="box">
|
||||
<view class="p-row-32 white b-r-8 m-t-3" v-if="info">
|
||||
<view class="p-col-32 bottom-border flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="肾功能" className="fs-3 tips-c"></d-text>
|
||||
<d-text text="正常" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<view class="p-col-32 bottom-border flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="过敏史" className="fs-3 tips-c"></d-text>
|
||||
<d-text text="阿莫西林" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<view class="p-col-32 bottom-border flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="个人病史" className="fs-3 tips-c"></d-text>
|
||||
<d-text text="无" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<view class="p-col-32 bottom-border flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text text="家族病史" className="fs-3 tips-c"></d-text>
|
||||
<d-text text="心脏病" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
baseInfo
|
||||
} from '@/api/all.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
info: ''
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
op['id']&&this.getInfo(op['id'])
|
||||
},
|
||||
methods: {
|
||||
getInfo(up_id=-1) {
|
||||
baseInfo({up_id}).then(() => {
|
||||
if (res.errcode == 0) {
|
||||
this.info = res.data
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
page {
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
.box {
|
||||
padding: 0 20rpx;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
@@ -6,31 +6,20 @@
|
||||
<u-tabs bar-width="20" :list="tabs.filter((item, index) => (type === 1 ? true : index !== 1))"
|
||||
:current="current" :is-scroll="false" @change="change" active-color="#6ACDBB"></u-tabs>
|
||||
</view>
|
||||
<view class="p-row-2" style="margin-top: 114rpx" v-if="!loading && list[current].length >= 0">
|
||||
<view class="p-32 white flex-col m-b-2" @click="toInfo(item.user_patient_id,item.id)"
|
||||
v-for="item in list[current]" :key="item.id">
|
||||
<view class="flex-row">
|
||||
<!-- 接诊状态 -->
|
||||
<!-- <d-text :color="tabs[current]['color']" :text="tabs[current]['name']"></d-text> -->
|
||||
<u-image width="104rpx" height="104rpx" 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="list">
|
||||
<view class="names">
|
||||
<text class="fs-32">{{item.patient}}</text>
|
||||
<text class="fs-24">{{item.created_at}}</text>
|
||||
</view>
|
||||
<view class="flex-row">
|
||||
<text>{{item.sex %2 != 0 ? '男' : '女'}}</text>
|
||||
<text>{{item.age + '岁'}}</text>
|
||||
</view>
|
||||
<view class="flex-row m-t-1">
|
||||
<text class="fs-24 color-gray">推广员:{{ item.salesperson_name || '无' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="margin-top: 114rpx; padding: 20rpx 24rpx 0;" v-if="!loading && list[current].length >= 0">
|
||||
<!-- 到店接诊列表卡:统一走 xk-list-card(信息分层 + 状态色 + 主/次操作按钮) -->
|
||||
<xk-list-card
|
||||
v-for="item in list[current]" :key="item.id"
|
||||
:avatar="item.avatar || require(`../../static/image/${item.sex%2==0?'nv':'nan'}.png`)"
|
||||
:title="item.patient"
|
||||
:meta="(item.sex % 2 != 0 ? '男' : '女') + ' · ' + item.age + '岁'"
|
||||
:time="item.created_at"
|
||||
:rows="['推广员:' + (item.salesperson_name || '无')]"
|
||||
:status="{ text: tabs[current].name, color: tabs[current].color }"
|
||||
:actions="[{ key: 'info', text: current === 0 ? '去接诊' : '查看详情', type: current === 0 ? 'primary' : 'ghost' }]"
|
||||
@action="toInfo(item.user_patient_id, item.id)"
|
||||
@click="toInfo(item.user_patient_id, item.id)"
|
||||
></xk-list-card>
|
||||
<u-loadmore :status="status" :load-text="loadText" v-if="!loading && list[current].length > 0" />
|
||||
</view>
|
||||
|
||||
@@ -234,24 +223,6 @@
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 30rpx;
|
||||
|
||||
.names {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
text {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.fun {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 按钮 -->
|
||||
<view class="head">
|
||||
<view class="head_left">
|
||||
<text :class="{
|
||||
active:!type
|
||||
}" @click="
|
||||
type = null;
|
||||
init();
|
||||
">全部({{ data.all_count }})</text>
|
||||
<text :class="{
|
||||
active:type==='high'
|
||||
}" @click="
|
||||
type = 'high';
|
||||
init();
|
||||
">高分({{ data.high_count }})</text>
|
||||
<text :class="{
|
||||
active:type==='low'
|
||||
}" @click="
|
||||
type = 'low';
|
||||
init();
|
||||
">低分({{ data.low_count }})</text>
|
||||
</view>
|
||||
|
||||
<view class="head_right">
|
||||
<view class="score">
|
||||
<u-icon name="star-fill" color="#FB8C00" size="36"></u-icon>
|
||||
<text>{{ data.score_avg }}</text>
|
||||
</view>
|
||||
<text>患者评分</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表 -->
|
||||
<view class="items" v-for="item in list" :key="item.id">
|
||||
<!-- <image src="../../static/logo.png" mode=""></image> -->
|
||||
<view class="items_right">
|
||||
<view class="item_name">
|
||||
<view class="title">
|
||||
<text>{{ item.nickname || "匿名" }}</text>
|
||||
<u-rate :count="count" v-model="item.score" size="28" active-color="#FB8C00"
|
||||
inactive-color="#b2b2b2" :gutter="item.score"></u-rate>
|
||||
</view>
|
||||
<view class="time"> 2023-1-13 </view>
|
||||
</view>
|
||||
<view class="info">
|
||||
非常非常耐心细致,是我最近遇到的最棒的医生,给了治疗建议,谢谢。
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<u-loadmore :status="status" :load-text="loadText" v-if="!loading && list.length > 0" />
|
||||
<view class="flex-row flex-jus-center" v-if="!loading && list.length == 0">
|
||||
<d-empty text="暂无信息" mode="search"></d-empty>
|
||||
</view>
|
||||
<d-loading-page :loading="loading"></d-loading-page>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
doctorWorkComments,
|
||||
commentMain
|
||||
} from "@/api/all.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
count: 5,
|
||||
type: null,
|
||||
data: {
|
||||
high_count: 0,
|
||||
low_count: 0,
|
||||
score_avg: 0,
|
||||
},
|
||||
page: 1,
|
||||
list: [],
|
||||
loading: true,
|
||||
loading2: false,
|
||||
status: "loading",
|
||||
loadText: {
|
||||
loadmore: "轻轻上拉",
|
||||
loading: "努力加载中",
|
||||
nomore: "实在没有了",
|
||||
},
|
||||
totalPage: 1,
|
||||
};
|
||||
},
|
||||
onShow() {
|
||||
this.getList();
|
||||
this.commentMain()
|
||||
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.init();
|
||||
},
|
||||
onReachBottom() {
|
||||
if (this.page >= this.totalPage) {
|
||||
this.$toast("数据已全部加载");
|
||||
return;
|
||||
}
|
||||
this.page++;
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
commentMain() {
|
||||
commentMain({store_id: uni.getStorageSync('store_id') || '11001'})
|
||||
.then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.data = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
init() {
|
||||
this.list = [];
|
||||
this.page = 1;
|
||||
(this.loading = true), this.getList(true);
|
||||
},
|
||||
|
||||
getList(is_stop = false) {
|
||||
if (this.loading2) {
|
||||
return;
|
||||
}
|
||||
this.loading2 = true;
|
||||
doctorWorkComments({
|
||||
page: this.page,
|
||||
type: this.type,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.list = [...res.data.list, ...this.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;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
.head {
|
||||
width: 750rpx;
|
||||
height: 110rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 32rpx 32rpx;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.head_left {
|
||||
.active {
|
||||
padding: 10rpx 20rpx;
|
||||
background: #6ACDBB;
|
||||
border-radius: 28rpx;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
text {
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 28rpx;
|
||||
background: #f1f5f9;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
font-size: 24rpx;
|
||||
color: #475569;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
margin: 0 20rpx 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.head_right {
|
||||
.score {
|
||||
text {
|
||||
height: 50rpx;
|
||||
font-size: 36rpx;
|
||||
font-family: PingFang SC-Semibold, PingFang SC;
|
||||
font-weight: 600;
|
||||
color: #fb8c00;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
}
|
||||
|
||||
text {
|
||||
height: 28rpx;
|
||||
font-size: 20rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items {
|
||||
width: 706rpx;
|
||||
display: flex;
|
||||
margin: 10rpx auto;
|
||||
background-color: #fff;
|
||||
justify-content: space-between;
|
||||
padding: 10rpx 10rpx;
|
||||
|
||||
image {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.items_right {
|
||||
.item_name {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
height: 34rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.time {
|
||||
height: 34rpx;
|
||||
font-size: 24rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin: 16rpx 0 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,97 +0,0 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="bg">
|
||||
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<view class="navbar" :style="[navbarHeight]"></view>
|
||||
<view class="title">
|
||||
</view>
|
||||
</view>
|
||||
<view class="box">
|
||||
<label class="box_title">请选择你的身份</label>
|
||||
<view class="box_item flex-row flex-ali-center flex-jus-sp" v-for="(item, index) in identitys" :key="index"
|
||||
@click="checkId(item)">
|
||||
<view class="flex-row flex-ali-center">
|
||||
<image :src="item.src"></image>
|
||||
<text>{{item.name}}</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" color="#A7ABB0" size="32"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
identitys: [{
|
||||
name: '我是医生',
|
||||
type: 1,
|
||||
src: '../../static/image/doctor.png'
|
||||
},
|
||||
{
|
||||
name: '我是药师',
|
||||
type: 2,
|
||||
src: '../../static/image/ys.png'
|
||||
}
|
||||
],
|
||||
statusBarHeight:this.$statusBarHeight,
|
||||
navbarHeight:this.$navbarHeight
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
checkId(item) {
|
||||
this.$go('/pages/login/register?role='+item.type)
|
||||
uni.setStorageSync('role', item.type)
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg {
|
||||
width: 100vw;
|
||||
background: linear-gradient(180deg, #EAF2FF 0%, rgba(238, 245, 255, 0) 100%);
|
||||
|
||||
.title {
|
||||
height: 200rpx;
|
||||
line-height: 200rpx;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 48rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #0D111A;
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 0 52rpx;
|
||||
|
||||
&_title {
|
||||
font-size: 36rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #6C7380;
|
||||
}
|
||||
|
||||
&_item {
|
||||
height: 152rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-radius: 8rpx;
|
||||
padding: 32rpx;
|
||||
margin-top: 32rpx;
|
||||
text{
|
||||
font-size: 36rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #31353D;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
image {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,83 +0,0 @@
|
||||
<template>
|
||||
<view class="inquiries safe-area-inset-bottom ff_pfsc p-row-32">
|
||||
<view class="p-col-16">
|
||||
<d-input prefixIcon="search" borderRadius="64rpx" v-model="keyword" type="text" placeholder="搜索疾病、症状等" height="66"></d-input>
|
||||
</view>
|
||||
<view class="list p-col-16 ">
|
||||
<view class="m-b-48" v-for="(item,index) in list">
|
||||
<view class="flex flex-jus-sp flex-ali-center">
|
||||
<view class="ask_l">
|
||||
问
|
||||
</view>
|
||||
<view class="ask_r content-c fs-32 text-twoline">
|
||||
小孩子5岁,左脸湿疹溃烂,之前肿胀流水,左下肢也有一些皮屑,5月25日初诊小孩子5岁,左脸湿疹溃烂,之前肿胀流水,左下肢也有一些皮屑,5月25日初诊
|
||||
</view>
|
||||
</view>
|
||||
<view class="answer text-hide">李磊:继续维持目前药物剂量,按照说明书的剂量服用</view>
|
||||
<view class="text-align-end m-t-16 fs-28">
|
||||
<text style="color: #2979FF;" v-if="item==1" @click.stop="setFine(1)">设为精选</text>
|
||||
<text style="color: #A7ABB0;" @click.stop="setFine(0)" v-else>取消精选</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
keyword:'',
|
||||
list:[1,2],
|
||||
type:'',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods:{
|
||||
setFine(type){
|
||||
// 1设为精选,0取消精选
|
||||
this.type=type
|
||||
},
|
||||
getList(){
|
||||
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
keyword(oldValue, newValue) {
|
||||
this.$u.debounce(this.getList, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.list {
|
||||
.ask_l {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #2979FF;
|
||||
color: #FFFFFF;
|
||||
font-size: 28rpx;
|
||||
line-height: 48rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ask_r {
|
||||
width: 622rpx;
|
||||
}
|
||||
|
||||
.answer {
|
||||
margin-top: 16rpx;
|
||||
background-color: #F3F4F5;
|
||||
height: 76rpx;
|
||||
font-size: 28rpx;
|
||||
color: #6C7380;
|
||||
padding: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<!-- <d-chat :state="type" :info="info" :record="record" v-if="record"></d-chat> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
type:0,
|
||||
info:{},
|
||||
page:1,
|
||||
record:'',
|
||||
a:"",
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
|
||||
},
|
||||
mounted() {
|
||||
// this.a = setInterval(() => {
|
||||
// this.messageList()
|
||||
// }, 1000)
|
||||
},
|
||||
destroyed() {
|
||||
// 取消监听键盘高度变化事件
|
||||
uni.offKeyboardHeightChange()
|
||||
// clearInterval(this.a)
|
||||
// this.a = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
page {
|
||||
background-color: #eeeeef;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,644 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom white">
|
||||
<view class="search">
|
||||
<u-search :height="66" :inputStyle="{height:'66rpx'}" @custom="keyword=''" :show-action="false"
|
||||
placeholder="请输入您想要的商品" v-model="keyword" @input="getDrugList"></u-search>
|
||||
</view>
|
||||
<view v-if="type==1">
|
||||
<u-tabs :list="tabs" :is-scroll="false" :current="current"
|
||||
@change="loading = true;current=$event;drugList($event)" bar-width="20" active-color="#6ACDBB"
|
||||
inactive-color="#6C7380">
|
||||
</u-tabs>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" style="padding-bottom: 200rpx">
|
||||
<!-- 西药 -->
|
||||
<block v-if="type==1">
|
||||
<view class="b-r-8 p-32" v-for="(item,i) in setList" :key="item.id">
|
||||
<view class="flex-row mb40">
|
||||
<u-image width="128rpx" height="144rpx" border-radius="8" :src="item.image">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<view class="m-l-24 flex-1">
|
||||
<view>
|
||||
<d-text :text="item.drug_name" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<d-text :text="'规格:'+item.specification" className="fs-24 tips-c mtb12"
|
||||
style="margin-left: 5rpx;"></d-text>
|
||||
<view class="flex-row flex-jus-sp flex-ali-center ">
|
||||
<view class="flex-row flex-1">
|
||||
<d-text :text="'库存:剩余'" className="fs-24 tips-c"></d-text>
|
||||
<d-text :text="item.stock" className="colorG fz24"></d-text>
|
||||
<d-text text="件" className="fs-24 tips-c"></d-text>
|
||||
</view>
|
||||
<view class="price">
|
||||
¥{{ parseFloat(item.price).toFixed(2) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t08 flex-row flex-jus-sp flex-ali-center">
|
||||
<view class="flex-row flex-1 flex-jus-sp ">
|
||||
<block v-if="!item.is_add">
|
||||
<view class="flex-row flex-ali-center btnBox" v-if="!item.is_collect"
|
||||
@click.prevent="hoarding(item.id,0)">
|
||||
<u-icon name="plus" color="#6ACDBB" size="26"></u-icon>
|
||||
<d-text :text="'常用药'" className="fs-24 ml14" color="#6ACDBB"></d-text>
|
||||
</view>
|
||||
<view v-else class="flex-row flex-ali-center btnBox del"
|
||||
@click.prevent="hoarding(item.id,1)">
|
||||
<u-icon name="minus-circle" color="#FC3636" size="30"></u-icon>
|
||||
<d-text :text="'移除'" className="fs-24 ml14" color="#FC3636"></d-text>
|
||||
</view>
|
||||
|
||||
<view class="m-l-32">
|
||||
<u-button :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" shape="circle"
|
||||
size="mini" @click="handleAdd(item, i)">
|
||||
添加商品
|
||||
</u-button>
|
||||
</view>
|
||||
</block>
|
||||
<d-number-box v-else v-model="item.number" :min="0" :max="item.stock"
|
||||
:asyncChange="true">
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${item.number>0?1:0.6})`}"
|
||||
@click="clickHandler('minus',item,item.number)">
|
||||
<u-icon name="minus" size="22" :color="item.number>0?'#000':'#A7ABB0'"></u-icon>
|
||||
</view>
|
||||
<view slot="input" style="width: 80rpx;"
|
||||
class="input flex-row flex-jus-center flex-ali-center">
|
||||
<d-text :text="item.number" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<view slot="plus" class="plus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(41,121,255,${item.number>=item.stock?0.6:1})`}"
|
||||
@click="clickHandler('plus',item,item.number)">
|
||||
<u-icon name="plus" size="22" color="#fff"></u-icon>
|
||||
</view>
|
||||
</d-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 中药 颗粒 -->
|
||||
<block v-if="type==2||type==3">
|
||||
<u-checkbox-group shape="circle" icon-size="35" size="44">
|
||||
<view v-for="(item, i) in list[0]" :key="item.id"
|
||||
class="flex-row flex-jus-sp white p-32 flex-ali-center" style="width: 100vw;">
|
||||
<u-checkbox :value="item.checked" @change="changeCheckbox(item,0)" :name="item.id">
|
||||
</u-checkbox>
|
||||
<view class="flex-col flex-1">
|
||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
||||
<d-text :text="item.drug_name||item.name" className="content-c fs-3"></d-text>
|
||||
<view class="flex-row flex-ali-end" v-if="item.price">
|
||||
<d-text
|
||||
:text="item.price.split('.').length>0?'¥':'¥'+item.price"
|
||||
className="fs-24 error-c">
|
||||
</d-text>
|
||||
<block v-if="item.price.split('.').length>0">
|
||||
<d-text :text="item.price.split('.')[0]" className="fs-3 error-c"
|
||||
:bold="true">
|
||||
</d-text>
|
||||
<d-text text="." className="fs-24 error-c"></d-text>
|
||||
<d-text :text="item.price.split('.')[1]"
|
||||
className="fs-24 error-c"></d-text>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<d-text :text="'单位:克'"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
</u-checkbox-group>
|
||||
<view class="bottom flex-row flex-jus-sp flex-ali-center" style="border-bottom: 2rpx solid #F2F3F5;">
|
||||
<d-text :text="`已选(${getSelect})`" className="fs-32 content-c" @click="affirmShow=true"></d-text>
|
||||
<u-button :throttle-time="0"
|
||||
:custom-style="{heigth:'86rpx',width:'478rpx',backgroundColor:'#6ACDBB',color:'#fff'}"
|
||||
shape="circle" @click="show=true;info=getList;" :disabled="!getSelect">
|
||||
下一步
|
||||
</u-button>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="flex-row flex-jus-center box"
|
||||
v-if="!loading&&((type==2||type==3)&&list[0].length==0||type==1&&setList.length==0)">
|
||||
<d-empty :text="keyword?'换个名称试试':current==1?'去收藏药品吧!':'暂无药品'" mode="search"></d-empty>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="btn" v-if="shownum">
|
||||
<u-button :throttle-time="0" :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" shape="circle"
|
||||
@click="toBack" :loading="loading">
|
||||
添加到处方
|
||||
</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 添加西药详情 -->
|
||||
<add-set-usage :info="info" @close="show=false"
|
||||
@add="index!=-1&&$set(list,index,$event);index=-1;show=false;type==1?drugList(current):$go(1, 4);recipe={}"
|
||||
:type="type" v-if="type>0&&show" :idx='idx' :recipe="recipe" :is_set="recipe['pId']?true:false">
|
||||
</add-set-usage>
|
||||
|
||||
<!-- 中药 颗粒 -->
|
||||
<u-popup v-model="affirmShow" mode="bottom" :closeable="true" @close="$emit('close')" close-icon-pos='top-right'
|
||||
:safe-area-inset-bottom="true" :mask-close-able="false">
|
||||
<view class="flex-row flex-jus-center flex-ali-center m-t-3">
|
||||
<d-text text="已选择" className="fs-32 tips-c"></d-text>
|
||||
</view>
|
||||
<view class="m-t-4 p-row-52 flex-col">
|
||||
<view class="bottom-border p-col-32 flex-row flex-jus-sp flex-ali-center"
|
||||
v-for="(item,index) in getList" :key="index">
|
||||
<d-text :text="item.drug_name" className="fs-32 content-c"></d-text>
|
||||
<d-text text="移除" className="fs-32 primary-c" @click="setChecked(item.id)"></d-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t-4 m-b-16">
|
||||
<u-button :throttle-time="0"
|
||||
:custom-style="{heigth:'86rpx',width:'646rpx',backgroundColor:'#6ACDBB',color:'#fff'}"
|
||||
shape="circle" @click="affirmShow=false" :disabled="!getSelect">
|
||||
确定
|
||||
</u-button>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixin from './mixin/mixin.js';
|
||||
import addSetUsage from './components/add-setUsage.vue'
|
||||
import {
|
||||
westList,
|
||||
chineseList,
|
||||
searchChinese,
|
||||
searchWest,
|
||||
westCommon,
|
||||
chineseCommon,
|
||||
westCollection,
|
||||
westUncollect,
|
||||
granularList,
|
||||
} from '@/api/all.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addSetUsage
|
||||
},
|
||||
mixins: [mixin],
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
recipe: {},
|
||||
affirmShow: false,
|
||||
index: -1,
|
||||
info: {},
|
||||
show: false,
|
||||
tabs: [{
|
||||
id: 0,
|
||||
name: '全部药品'
|
||||
}, {
|
||||
id: 1,
|
||||
name: '我的常用药'
|
||||
}],
|
||||
list: {
|
||||
0: [],
|
||||
1: [],
|
||||
},
|
||||
keyword: '',
|
||||
current: 0,
|
||||
type: 0,
|
||||
searchList: {
|
||||
0: [],
|
||||
1: [],
|
||||
},
|
||||
idx: 0,
|
||||
shownum: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
setList() {
|
||||
this.getDrug();
|
||||
let list = this.list[this.current].length > 0 ? this.list[this.current] : [];
|
||||
let arr = [];
|
||||
this.type == 1 && (arr = this.wests)
|
||||
this.type == 2 && (arr = this.chineses)
|
||||
this.type == 3 && (arr = this.granular)
|
||||
// console.log(arr, 'this.granular')
|
||||
list = list.map((item) => {
|
||||
let it = arr.find(it => it['id'] == item.id) || {}
|
||||
const obj = {
|
||||
...item,
|
||||
...it,
|
||||
is_add: it['id'] ? true : false,
|
||||
stock: item['stock'] - (it['number'] || 0)
|
||||
}
|
||||
return obj
|
||||
});
|
||||
return list
|
||||
},
|
||||
getList() {
|
||||
return this.list[this.current].filter(item => item.checked).map(item => {
|
||||
return {
|
||||
...item,
|
||||
grammage: item.grammage || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
getSelect() {
|
||||
return this.list[this.current].filter(item => item.checked).length
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
this.current = op['current'] || 0;
|
||||
this.type = op['type'] || 0;
|
||||
this.idx = op['index'] || 0;
|
||||
this.recipe = op['recipe'] ? JSON.parse(op['recipe']) : {}
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
this.drugList();
|
||||
this.getDrug();
|
||||
},
|
||||
methods: {
|
||||
toBack() {
|
||||
uni.navigateBack(1)
|
||||
},
|
||||
changeCheckbox(e, cur) {
|
||||
this.list[cur] = this.list[cur].map(item => {
|
||||
if (item.id === e.id) {
|
||||
return {
|
||||
...item,
|
||||
checked: !item.checked
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
async hoarding(id, type = 0) {
|
||||
let res = {}
|
||||
uni.showLoading({
|
||||
title: '请稍后...'
|
||||
})
|
||||
type == 0 && (res = await westCollection({
|
||||
id,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
type == 1 && (res = await westUncollect({
|
||||
id,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
if (res.errcode == 0) {
|
||||
this.list[this.current] = this.list[this.current].map((it) => {
|
||||
return {
|
||||
...it,
|
||||
is_collect: id == it['id'] ? !it.is_collect : it.is_collect
|
||||
}
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
} else {
|
||||
this.$toast(res.msg)
|
||||
}
|
||||
},
|
||||
getDrugList() {
|
||||
uni.showLoading({
|
||||
title: '搜索中...'
|
||||
})
|
||||
this.loading = true
|
||||
uni.$u.debounce(this.drugList, 1000);
|
||||
},
|
||||
// 药品列表
|
||||
async drugList(current = 0) {
|
||||
this.list[this.current] = []
|
||||
let res = {}
|
||||
if (current == 0) {
|
||||
// 西药列表
|
||||
this.type == 1 && (res = await westList({
|
||||
name: this.keyword,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
// 中药列表
|
||||
this.type == 2 && (res = await chineseList({
|
||||
name: this.keyword,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
this.type == 3 && (res = await granularList({
|
||||
name: this.keyword,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
} else {
|
||||
// 常用药-西药
|
||||
this.type == 1 && (res = await westCommon({
|
||||
name: this.keyword,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
// 常用药-中药
|
||||
this.type == 2 && (res = await chineseCommon({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
}
|
||||
if (res.errcode == 0) {
|
||||
if (this.type == 2) {
|
||||
this.list[this.current] = res.data
|
||||
for (var i = 0; i < this.list[this.current].length; i++) {
|
||||
let id = this.list[this.current][i]['id']
|
||||
let chinese = this.chineses[this.idx]['item'].find(it => it['id'] == id) || {}
|
||||
if (id == chinese['id']) {
|
||||
this.list[this.current][i] = chinese
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.type == 3) {
|
||||
this.list[this.current] = res.data
|
||||
for (var i = 0; i < this.list[this.current].length; i++) {
|
||||
let id = this.list[this.current][i]['id']
|
||||
let granular = this.granular[this.idx]['item'].find(it => it['id'] == id) || {}
|
||||
if (id == granular['id']) {
|
||||
this.list[this.current][i] = granular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.type == 1) {
|
||||
let collect = Array.isArray(res.data) ? res.data : res.data.collect || res.data.collected;
|
||||
collect = (collect || []).map(item => {
|
||||
return {
|
||||
...item,
|
||||
is_collect: true
|
||||
}
|
||||
})
|
||||
let no_collect = res.data.no_collect || []
|
||||
let uncollect = res.data.uncollect || []
|
||||
uncollect = [...uncollect, ...no_collect].map(item => {
|
||||
return {
|
||||
...item,
|
||||
is_collect: false
|
||||
}
|
||||
})
|
||||
this.list[this.current] = collect.concat(this.current == 0 ? uncollect : [])
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
uni.hideLoading()
|
||||
})
|
||||
} else {
|
||||
this.loading = false
|
||||
this.$toast(res.msg)
|
||||
}
|
||||
},
|
||||
clickHandler(type, data, number) {
|
||||
// TODO 这里有Bug
|
||||
let that = this;
|
||||
let item = JSON.parse(JSON.stringify(data));
|
||||
item['number'] = number || item['number']
|
||||
if (type === 'minus' && item['number'] == 1) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '是否确认从处方中删除该药品',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 根据item的id找到对应的索引
|
||||
let index = that.wests.findIndex(it => it['id'] == item['id'])
|
||||
// 定义变量type,根据tabsActive的值决定数组操作的类型
|
||||
let type = that.type;
|
||||
switch (type) {
|
||||
case 1:
|
||||
index = that.wests.findIndex(it => it['id'] == item['id'])
|
||||
break;
|
||||
case 2:
|
||||
index = that.chineses.findIndex(it => it['id'] == item['id'])
|
||||
break;
|
||||
case 3:
|
||||
index = that.granular.findIndex(it => it['id'] == item['id'])
|
||||
break;
|
||||
}
|
||||
console.log(index, 'sssssssssss')
|
||||
// 如果index有效,则执行删除和存储操作
|
||||
if (index >= 0) {
|
||||
// 根据type的值,从对应的数组中删除指定索引的元素
|
||||
type == 1 && that.wests.splice(index, 1);
|
||||
type == 2 && that.chineses.splice(index, 1);
|
||||
type == 3 && that.granular.splice(index, 1);
|
||||
// 定义变量key用于存储缓存键名
|
||||
let key = null
|
||||
// 根据type和缓存标志决定key的值
|
||||
if (type == 1) {
|
||||
key = uni.getStorageSync('add') ? 'addWests' : 'wests'
|
||||
}
|
||||
if (type == 2) {
|
||||
key = uni.getStorageSync('add') ? 'addChineses' : 'chineses'
|
||||
}
|
||||
if (type == 3) {
|
||||
key = uni.getStorageSync('add') ? 'addGranular' : 'granular'
|
||||
}
|
||||
// 将修改后的数组存储到缓存中,如果数组为空则存储默认值
|
||||
uni.setStorageSync(key, type == 1 ? this.wests : type == 2 ? this.chineses.length == 0 ? [{
|
||||
item: [],
|
||||
obj: {}
|
||||
}] : this.chineses : this.granular.length == 0 ? [{
|
||||
item: [],
|
||||
obj: {}
|
||||
}] : this.granular)
|
||||
// 调用getDrug方法,可能用于更新药物数据
|
||||
that.getDrug()
|
||||
} else {
|
||||
// 如果index无效,清空所有数组和相关缓存
|
||||
this.wests = []
|
||||
this.chineses = []
|
||||
this.granular = []
|
||||
uni.removeStorageSync(uni.getStorageSync('add') ? 'addWests' : 'wests')
|
||||
// 日志输出,可能用于调试
|
||||
console.log(1);
|
||||
// 同样,设置缓存,存储空的数组结构
|
||||
uni.setStorageSync(uni.getStorageSync('add') ? 'addChineses' : 'chineses', [{
|
||||
item: [],
|
||||
obj: {}
|
||||
}])
|
||||
uni.setStorageSync(uni.getStorageSync('add') ? 'addGranular' : 'granular', [{
|
||||
item: [],
|
||||
obj: {}
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.$set(item, 'number', item['number'])
|
||||
return
|
||||
}
|
||||
item['number'] != 0 && type === 'minus' && item['number']--;
|
||||
item['number'] < item.stock && type === 'plus' && item['number']++
|
||||
this.setDrug(item);
|
||||
},
|
||||
|
||||
// 添加西药
|
||||
handleAdd(item, i) {
|
||||
// console.log(item, 'item')
|
||||
this.show = true
|
||||
this.info = {
|
||||
...item
|
||||
}
|
||||
this.index = i
|
||||
this.shownum = true
|
||||
},
|
||||
setChecked(id) {
|
||||
this.list[this.current] = this.list[this.current].map(item => {
|
||||
if (item.id == id) {
|
||||
item.checked = !item.checked
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search {
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 710rpx;
|
||||
margin: 20rpx auto;
|
||||
position: fixed;
|
||||
left: 2.2%;
|
||||
bottom: 20rpx;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.minus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-top-left-radius: 8rpx;
|
||||
border-bottom-left-radius: 8rpx;
|
||||
}
|
||||
|
||||
.plus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #2979FF;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 8rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 122rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F3F4F5;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
padding: 12rpx 32rpx;
|
||||
|
||||
::v-deep button {
|
||||
z-index: 999;
|
||||
background: #6ACDBB;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.m-b-16 {
|
||||
::v-deep button {
|
||||
z-index: 999;
|
||||
background: #6ACDBB;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 122rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 32rpx;
|
||||
color: #F44437;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.colorG {
|
||||
color: #6ACDBB !important;
|
||||
|
||||
}
|
||||
|
||||
.fz24 {
|
||||
font-size: 24rpx !important;
|
||||
}
|
||||
|
||||
.minus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-top-left-radius: 8rpx;
|
||||
border-bottom-left-radius: 8rpx;
|
||||
}
|
||||
|
||||
.plus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #6ACDBB;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 8rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 122rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F3F4F5;
|
||||
}
|
||||
|
||||
.mtb12 {
|
||||
margin: 12rpx 0;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
padding: 12rpx 32rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 122rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
.btnBox {
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid #6ACDBB;
|
||||
|
||||
&.del {
|
||||
border: 2rpx solid #FC3636;
|
||||
}
|
||||
}
|
||||
|
||||
.mb40 {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.ml14 {
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,476 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom white">
|
||||
<view class="search">
|
||||
<u-search :height="66" :inputStyle="{height:'66rpx'}" @custom="keyword=''" :show-action="false"
|
||||
placeholder="请输入您想要的商品" v-model="keyword" @input="getDrugList"></u-search>
|
||||
</view>
|
||||
<scroll-view scroll-y="true" style="padding-bottom: 200rpx">
|
||||
<!-- 西药 -->
|
||||
<block v-if="type==3">
|
||||
<view class="b-r-8 p-32" v-for="(item,i) in setList" :key="item.id">
|
||||
<view class="flex-row mb40">
|
||||
<u-image width="128rpx" height="144rpx" border-radius="8" :src="item.image">
|
||||
<u-loading slot="loading"></u-loading>
|
||||
</u-image>
|
||||
<view class="m-l-24 flex-1">
|
||||
<view>
|
||||
<d-text :text="item.drug_name" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
<d-text :text="'规格:'+item.specification" className="fs-24 tips-c mtb12"
|
||||
style="margin-left: 5rpx;"></d-text>
|
||||
<view class="flex-row flex-jus-sp flex-ali-center ">
|
||||
<view class="flex-row flex-1">
|
||||
<d-text :text="'库存:剩余'" className="fs-24 tips-c"></d-text>
|
||||
<d-text :text="item.stock" className="colorG fz24"></d-text>
|
||||
<d-text text="件" className="fs-24 tips-c"></d-text>
|
||||
</view>
|
||||
<view class="price">
|
||||
¥{{ parseFloat(item.price).toFixed(2) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t08 flex-row flex-jus-sp flex-ali-center">
|
||||
<view class="flex-row flex-1 flex-jus-sp ">
|
||||
<block v-if="!item.is_add">
|
||||
<view class="flex-row flex-ali-center btnBox">
|
||||
<u-icon name="plus" color="#6ACDBB" size="26"></u-icon>
|
||||
<d-text :text="'未添加'" className="fs-24 ml14" color="#6ACDBB"></d-text>
|
||||
</view>
|
||||
|
||||
<view class="m-l-32">
|
||||
<u-button :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" shape="circle"
|
||||
size="mini" @click="handleAdd(item, i)">
|
||||
添加商品
|
||||
</u-button>
|
||||
</view>
|
||||
</block>
|
||||
<!-- <view v-else class="flex-row flex-ali-center btnBox">-->
|
||||
<!-- <d-text :text="`已添加(${item.number})周期`" className="fs-24 ml14" color="#6ACDBB"></d-text>-->
|
||||
<!-- </view>-->
|
||||
<d-number-box v-else v-model="item.number" :min="0" :max="item.stock"
|
||||
:asyncChange="true">
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${item.number>0?1:0.6})`}"
|
||||
@click="clickHandler('minus',item,item.number)">
|
||||
<u-icon name="minus" size="22" :color="item.number>0?'#000':'#A7ABB0'"></u-icon>
|
||||
</view>
|
||||
<view slot="input" style="width: 80rpx;"
|
||||
class="input flex-row flex-jus-center flex-ali-center">
|
||||
<d-text :text="item.number" className="fs-3 content-c"></d-text>
|
||||
</view>
|
||||
</d-number-box>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view class="flex-row flex-jus-center box"
|
||||
v-if="!loading&&((type===2||type===3)&&list[0].length===0||type===1&&setList.length===0)">
|
||||
<d-empty :text="keyword?'换个名称试试':current===1?'去收藏药品吧!':'暂无药品'" mode="search"></d-empty>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="btn" v-if="shownum">
|
||||
<u-button :throttle-time="0" :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" shape="circle"
|
||||
@click="toBack" :loading="loading">
|
||||
添加到处方
|
||||
</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 添加西药详情 -->
|
||||
<add-set-usage :info="info" @close="show=false"
|
||||
@add="index!==-1&&$set(list,index,$event);index=-1;show=false;type===1?drugList(current):$go(1, 4);recipe={}"
|
||||
:type="type" v-if="type>0&&show" :idx='idx' :recipe="recipe" :is_set="!!recipe['pId']">
|
||||
</add-set-usage>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixin from './mixin/mixin.js';
|
||||
import addSetUsage from './components/add-setUsage.vue'
|
||||
import {servicePageList,} from '@/api/servicePackage.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
addSetUsage
|
||||
},
|
||||
mixins: [mixin],
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
recipe: {},
|
||||
affirmShow: false,
|
||||
index: -1,
|
||||
info: {},
|
||||
show: false,
|
||||
tabs: [{
|
||||
id: 0,
|
||||
name: '全部药品'
|
||||
}, {
|
||||
id: 1,
|
||||
name: '我的常用药'
|
||||
}],
|
||||
list: {
|
||||
0: [],
|
||||
1: [],
|
||||
},
|
||||
keyword: '',
|
||||
current: 0,
|
||||
type: 0,
|
||||
searchList: {
|
||||
0: [],
|
||||
1: [],
|
||||
},
|
||||
idx: 0,
|
||||
shownum: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
setList() {
|
||||
this.getDrug();
|
||||
let list = this.list[this.current]?.length > 0 ? this.list[this.current] : [];
|
||||
let arr = [];
|
||||
arr = this.servicePackage
|
||||
console.log(list, 'this.servicePackage')
|
||||
console.log(arr, 'this.servicePackage')
|
||||
list = list.map((item) => {
|
||||
let it = arr.find(it => it['id'] === item.id) || {};
|
||||
return {
|
||||
...item,
|
||||
...it,
|
||||
is_add: !!it['id'],
|
||||
stock: item['stock'] - (it['number'] || 0)
|
||||
}
|
||||
});
|
||||
return list
|
||||
},
|
||||
getList() {
|
||||
return this.list[this.current].filter(item => item.checked).map(item => {
|
||||
return {
|
||||
...item,
|
||||
grammage: item.grammage || 0
|
||||
}
|
||||
})
|
||||
},
|
||||
getSelect() {
|
||||
return this.list[this.current].filter(item => item.checked).length
|
||||
}
|
||||
},
|
||||
onLoad(op) {
|
||||
this.current = op['current'] || 0;
|
||||
this.type = 3;
|
||||
this.idx = op['index'] || 0;
|
||||
this.recipe = op['recipe'] ? JSON.parse(op['recipe']) : {}
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
})
|
||||
this.drugList();
|
||||
this.getDrug();
|
||||
},
|
||||
methods: {
|
||||
toBack() {
|
||||
uni.navigateBack(1)
|
||||
},
|
||||
changeCheckbox(e, cur) {
|
||||
this.list[cur] = this.list[cur].map(item => {
|
||||
if (item.id === e.id) {
|
||||
return {
|
||||
...item,
|
||||
checked: !item.checked
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
async hoarding(id, type = 0) {
|
||||
let res = {}
|
||||
uni.showLoading({
|
||||
title: '请稍后...'
|
||||
})
|
||||
// type == 0 && (res = await westCollection({
|
||||
// id,
|
||||
// store_id: uni.getStorageSync('store_id') || '11001'
|
||||
// }))
|
||||
// type == 1 && (res = await westUncollect({
|
||||
// id,
|
||||
// store_id: uni.getStorageSync('store_id') || '11001'
|
||||
// }))
|
||||
if (res.errcode === 0) {
|
||||
this.list[this.current] = this.list[this.current].map((it) => {
|
||||
return {
|
||||
...it,
|
||||
is_collect: id === it['id'] ? !it.is_collect : it.is_collect
|
||||
}
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
} else {
|
||||
this.$toast(res.msg)
|
||||
}
|
||||
},
|
||||
getDrugList() {
|
||||
uni.showLoading({
|
||||
title: '搜索中...'
|
||||
})
|
||||
this.loading = true
|
||||
uni.$u.debounce(this.drugList, 1000);
|
||||
},
|
||||
// 药品列表
|
||||
async drugList() {
|
||||
this.list[this.current] = [];
|
||||
let res = {};
|
||||
|
||||
(res = await servicePageList({
|
||||
name: this.keyword,
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}))
|
||||
|
||||
if (res.errcode === 0) {
|
||||
let collect = Array.isArray(res.data) ? res.data : res.data.collect || res.data.collected;
|
||||
collect = (collect || []).map(item => {
|
||||
return {
|
||||
...item,
|
||||
is_collect: true
|
||||
}
|
||||
})
|
||||
let no_collect = res.data.no_collect || []
|
||||
let uncollect = res.data.uncollect || []
|
||||
uncollect = [...uncollect, ...no_collect].map(item => {
|
||||
return {
|
||||
...item,
|
||||
is_collect: false
|
||||
}
|
||||
})
|
||||
this.list[this.current] = collect.concat(this.current == 0 ? uncollect : [])
|
||||
console.log(this.list[this.current], 'sssssssssssssssssssssss')
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
uni.hideLoading()
|
||||
})
|
||||
} else {
|
||||
this.loading = false
|
||||
this.$toast(res.msg)
|
||||
}
|
||||
},
|
||||
clickHandler(type, data, number) {
|
||||
let that = this;
|
||||
let item = JSON.parse(JSON.stringify(data));
|
||||
item['number'] = number || item['number']
|
||||
if (type === 'minus' && item['number'] == 1) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '是否确认从处方中删除该药品',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 根据item的id找到对应的索引
|
||||
let type = that.type;
|
||||
let index = that.servicePackage.findIndex(it => it.id === item.id)
|
||||
// 如果index有效,则执行删除和存储操作
|
||||
if (index >= 0) {
|
||||
// 根据type的值,从对应的数组中删除指定索引的元素
|
||||
that.servicePackage.splice(index, 1);
|
||||
// 定义变量key用于存储缓存键名
|
||||
let key = null
|
||||
key = uni.getStorageSync('add') ? 'addServicePackage' : 'servicePackage'
|
||||
// 将修改后的数组存储到缓存中,如果数组为空则存储默认值
|
||||
uni.setStorageSync(key, this.servicePackage)
|
||||
// 调用getDrug方法,可能用于更新药物数据
|
||||
that.getDrug()
|
||||
} else {
|
||||
// 如果index无效,清空所有数组和相关缓存
|
||||
this.servicePackage = []
|
||||
uni.removeStorageSync(uni.getStorageSync('add') ? 'addServicePackage' : 'servicePackage')
|
||||
// 同样,设置缓存,存储空的数组结构
|
||||
uni.setStorageSync(uni.getStorageSync('add') ? 'addServicePackage' : 'servicePackage', [{
|
||||
item: [],
|
||||
obj: {}
|
||||
}])
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.$set(item, 'number', item['number'])
|
||||
return
|
||||
}
|
||||
item['number'] != 0 && type === 'minus' && item['number']--;
|
||||
item['number'] < item.stock && type === 'plus' && item['number']++
|
||||
this.setDrug(item);
|
||||
},
|
||||
|
||||
// 添加西药
|
||||
handleAdd(item, i) {
|
||||
// console.log(item, 'item')
|
||||
this.show = true
|
||||
this.info = {
|
||||
...item
|
||||
}
|
||||
this.index = i
|
||||
this.shownum = true
|
||||
},
|
||||
setChecked(id) {
|
||||
this.list[this.current] = this.list[this.current].map(item => {
|
||||
if (item.id == id) {
|
||||
item.checked = !item.checked
|
||||
}
|
||||
return item
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search {
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 710rpx;
|
||||
margin: 20rpx auto;
|
||||
position: fixed;
|
||||
left: 2.2%;
|
||||
bottom: 20rpx;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.minus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-top-left-radius: 8rpx;
|
||||
border-bottom-left-radius: 8rpx;
|
||||
}
|
||||
|
||||
.plus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #2979FF;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 8rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 122rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F3F4F5;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
padding: 12rpx 32rpx;
|
||||
|
||||
::v-deep button {
|
||||
z-index: 999;
|
||||
background: #6ACDBB;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.m-b-16 {
|
||||
::v-deep button {
|
||||
z-index: 999;
|
||||
background: #6ACDBB;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 122rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 32rpx;
|
||||
color: #F44437;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.search {
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.colorG {
|
||||
color: #6ACDBB !important;
|
||||
|
||||
}
|
||||
|
||||
.fz24 {
|
||||
font-size: 24rpx !important;
|
||||
}
|
||||
|
||||
.minus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-top-left-radius: 8rpx;
|
||||
border-bottom-left-radius: 8rpx;
|
||||
}
|
||||
|
||||
.plus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #6ACDBB;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 8rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
width: 122rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F3F4F5;
|
||||
}
|
||||
|
||||
.mtb12 {
|
||||
margin: 12rpx 0;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
padding: 12rpx 32rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 122rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #A7ABB0;
|
||||
text-align-last: justify;
|
||||
}
|
||||
|
||||
.btnBox {
|
||||
height: 48rpx;
|
||||
line-height: 48rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid #6ACDBB;
|
||||
|
||||
&.del {
|
||||
border: 2rpx solid #FC3636;
|
||||
}
|
||||
}
|
||||
|
||||
.mb40 {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.ml14 {
|
||||
margin-left: 14rpx;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,804 +0,0 @@
|
||||
<template>
|
||||
<view class="box">
|
||||
<u-popup v-model="show" mode="bottom" :closeable="true" @close="$emit('close')" close-icon-pos='top-right'
|
||||
:safe-area-inset-bottom="true" :mask-close-able="false" z-index="10078">
|
||||
<scroll-view scroll-y="true"
|
||||
:style="{height: `calc(100vh - 110rpx - env(safe-area-inset-bottom) - var(--status-bar-height) - ${navbarHeight}px)`}">
|
||||
<!-- 西药详情 -->
|
||||
<view class="box flex-col white">
|
||||
<view class="flex-row flex-jus-center">
|
||||
<d-text text="用法用量" className="fs-32 main-c m-t-3"></d-text>
|
||||
</view>
|
||||
<view class="flex-col m-t-32 p-32" v-if="type==1">
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">名称:</view>
|
||||
<view class="val">{{obj.drug_name}}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">规格:</view>
|
||||
<view class="val">{{obj.specification}}</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="label">库存:</view>
|
||||
<view class="val">剩余{{obj.stock}}件</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox" v-if="see_rate==1">
|
||||
<view class="item">
|
||||
<view class="label">毛利率:</view>
|
||||
<view class="val">{{getRate()}}%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">厂家:</view>
|
||||
<view class="val">{{obj.source}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">功能主治:</view>
|
||||
<view class="val">{{obj.function}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t-24 flex-col bg p-col-08 iBox">
|
||||
<d-text text="用法用量(说明书)" className="fs-24 content-c m-l-16"></d-text>
|
||||
<d-text :text="obj.usage" className="fs-24 tips-c m-l-16"></d-text>
|
||||
<image src="../../../../static/image/i4.png" class="icon" mode="scaleToFill"
|
||||
@click="previewImage(obj.instruction)" />
|
||||
</view>
|
||||
<view class="flex-col flex-jus-sp m-t-32">
|
||||
<d-text text="用法用量" className="fs-3 main-c"></d-text>
|
||||
<d-text text="初始默认值为说明书用量,请根据患者实际情况调整剂量" className="fs-24 error-c m-t08"></d-text>
|
||||
|
||||
</view>
|
||||
<view class="flex-row flex-jus-sp m-t-32">
|
||||
<d-text text="药品数量" className="fs-3 main-c"></d-text>
|
||||
<d-number-box v-model="obj.number" :min="1" :max="obj.stock">
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${obj.number>1?1:0.6})`}">
|
||||
<u-icon name="minus" size="22" :color="obj.number>1?'#000':'#A7ABB0'"></u-icon>
|
||||
</view>
|
||||
|
||||
<view slot="plus" class="plus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(41,121,255,${obj.number>=obj.stock?0.6:1})`}">
|
||||
<u-icon name="plus" size="22" color="#fff"></u-icon>
|
||||
</view>
|
||||
</d-number-box>
|
||||
</view>
|
||||
<view class="bottom-border"></view>
|
||||
<view class="flex-col">
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">用法:</text>
|
||||
<view class="flex-row flex-ali-center">
|
||||
<view class="bg p-row-16">
|
||||
<u-input v-model="obj.usages['0']['name']" type="select"
|
||||
:select-open="key=='time'" @click="key='time';pickerShow=true"
|
||||
placeholder="请选择" />
|
||||
</view>
|
||||
<view class="bg m-l-32 p-row-16">
|
||||
<u-input v-model="obj.usages['1']['name']" type="select"
|
||||
:select-open="key=='usage'" @click="key='usage';pickerShow=true"
|
||||
placeholder="请选择" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">每次:</text>
|
||||
<view class="flex-row flex-ali-center">
|
||||
<d-number-box v-model="obj.count" :min="1">
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${obj.count>1?1:0.6})`}">
|
||||
<u-icon name="minus" size="22" :bold="true"
|
||||
:color="`rgba(41,121,255,${obj.count>1?1:0.6})`"></u-icon>
|
||||
</view>
|
||||
|
||||
<view slot="plus" class="plus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${obj.count>=obj.stock?0.6:1})`}">
|
||||
<u-icon name="plus" size="22" :bold="true"
|
||||
:color="`rgba(41,121,255,${obj.count>=obj.stock?0.6:1})`"></u-icon>
|
||||
</view>
|
||||
</d-number-box>
|
||||
<view class="bg m-l-32 p-row-16">
|
||||
<u-input v-model="obj.describe['name']" type="select"
|
||||
:select-open="key=='describe'" @click="key='describe';pickerShow=true;"
|
||||
placeholder="请选择" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">频率:</text>
|
||||
<view class="bg p-row-16">
|
||||
<u-input v-model="obj.frequency['name']" type="select"
|
||||
:select-open="key=='frequency'" @click="key='frequency';pickerShow=true;"
|
||||
placeholder="请选择" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
服务包
|
||||
<view class="flex-col m-t-32 p-32" v-if="type==3">
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">名称:</view>
|
||||
<view class="val">{{obj.drug_name}}</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">规格:</view>
|
||||
<view class="val">{{obj.specification}}</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="label">库存:</view>
|
||||
<view class="val">剩余{{obj.stock}}件</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox" v-if="see_rate==1">
|
||||
<view class="item">
|
||||
<view class="label">毛利率:</view>
|
||||
<view class="val">{{getRate()}}%</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">厂家:</view>
|
||||
<view class="val">{{obj.source}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flexBox">
|
||||
<view class="item">
|
||||
<view class="label">功能主治:</view>
|
||||
<view class="val">{{obj.function}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="m-t-24 flex-col bg p-col-08 iBox">
|
||||
<d-text text="用法用量(说明书)" className="fs-24 content-c m-l-16"></d-text>
|
||||
<d-text :text="obj.usage" className="fs-24 tips-c m-l-16"></d-text>
|
||||
<image src="../../../../static/image/i4.png" class="icon" mode="scaleToFill"
|
||||
@click="previewImage(obj.instruction)" />
|
||||
</view>
|
||||
<view class="flex-col flex-jus-sp m-t-32">
|
||||
<d-text text="用法用量" className="fs-3 main-c"></d-text>
|
||||
<d-text text="初始默认值为说明书用量,请根据患者实际情况调整剂量" className="fs-24 error-c m-t08"></d-text>
|
||||
|
||||
</view>
|
||||
<view class="flex-row flex-jus-sp m-t-32">
|
||||
<d-text text="商品数量" className="fs-3 main-c"></d-text>
|
||||
<d-number-box v-model="obj.number" :min="1" :max="1">
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${obj.number>1?1:0.6})`}">
|
||||
<u-icon name="minus" size="22" :color="obj.number>1?'#000':'#A7ABB0'"></u-icon>
|
||||
</view>
|
||||
|
||||
<view slot="plus" class="plus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(41,121,255,${obj.number>=obj.stock?0.6:1})`}">
|
||||
<u-icon name="plus" size="22" color="#fff"></u-icon>
|
||||
</view>
|
||||
</d-number-box>
|
||||
</view>
|
||||
<view class="bottom-border"></view>
|
||||
<view class="flex-col">
|
||||
</view>
|
||||
</view>
|
||||
<!-- 中药 -->
|
||||
<view class="box flex-col white p-32" v-if="(type==2)&&is_next">
|
||||
<view class="grid-tem-col-2 rp bg p-16">
|
||||
<template v-for="item in obj">
|
||||
<view class="flex-row flex-jus-sp flex-ali-center" :key="item.id" v-if="item.checked">
|
||||
<d-text :text="item.drug_name" color="#31353D"></d-text>
|
||||
<d-text :text="item.grammage+'g'" color="#6C7380"></d-text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<view class="flex-row m-t-32 flex-ali-center">
|
||||
<text class="text">调配:</text>
|
||||
<view class="flex-row flex-ali-center">
|
||||
<view class="subsection" v-if="show">
|
||||
<u-subsection :height="50" active-color="#6ACDBB" font-size="24" :list="recipes"
|
||||
:current="addRecipe.deploy||0" mode="subsection"
|
||||
@change="addRecipe.deploy=$event.index"></u-subsection>
|
||||
</view>
|
||||
<view class="m-l-32">
|
||||
<u-checkbox-group shape="circle" icon-size="28" size="36">
|
||||
<u-checkbox v-model="addRecipe['frying']" name="浓煎"
|
||||
@change="$set(addRecipe,'frying',$event.value)">浓煎</u-checkbox>
|
||||
</u-checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">剂数:</text>
|
||||
<view class="bg p-row-16 flex-1">
|
||||
<u-input v-model="addRecipe.dosage['name']" type="select" :select-open="key=='dosage'"
|
||||
placeholder="请选择" @click="key='dosage';pickerShow=true" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">用量:</text>
|
||||
<view class="bg p-row-16 flex-1">
|
||||
<u-input v-model="addRecipe.nums['name']" type="select" :select-open="key=='nums'"
|
||||
placeholder="请选择" @click="key='nums';pickerShow=true" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">用法:</text>
|
||||
<view class="bg p-row-16 flex-1">
|
||||
<u-input v-model="addRecipe.usage['name']" type="select" :select-open="key=='usage'"
|
||||
placeholder="请选择" @click="key='usage';pickerShow=true" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">服法:</text>
|
||||
<view class="bg p-row-16 flex-1">
|
||||
<u-input v-model="addRecipe.time['name']" type="select" :select-open="key=='time'"
|
||||
placeholder="请选择" @click="key='time';pickerShow=true" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-row flex-ali-center m-t-32">
|
||||
<text class="text">备注:</text>
|
||||
<view class="bg p-row-16 flex-1">
|
||||
<u-input v-model="addRecipe.remark" placeholder="请输入" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 颗粒 -->
|
||||
<view class="p-row-52 flex-col" v-if="(type==2||type==4)&&!is_next">
|
||||
<view class="bottom-border p-col-32 flex-row flex-jus-sp flex-ali-center"
|
||||
v-for="(item,index) in obj" :key="index">
|
||||
<d-text :text="item.drug_name" className="fs-32 content-c"></d-text>
|
||||
<d-number-box v-model="item.grammage" :min='0'>
|
||||
<view slot="minus" class="minus flex-row flex-jus-center flex-ali-center"
|
||||
:style="{backgroundColor:`rgba(249, 250, 251,${item.grammage>0?1:0.6})`}">
|
||||
<u-icon name="minus" size="22" :color="item.grammage>0?'#000':'#A7ABB0'"></u-icon>
|
||||
</view>
|
||||
|
||||
<view slot="plus" class="plus flex-row flex-jus-center flex-ali-center primary-c">
|
||||
<u-icon name="plus" size="22" color="#fff"></u-icon>
|
||||
</view>
|
||||
</d-number-box>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="add">
|
||||
<u-button :throttle-time="0" :custom-style="{ backgroundColor:'#6ACDBB',color:'#fff' }" shape="circle"
|
||||
@click="(type==2||type==4)&&!is_next?next():add()" :loading="loading">
|
||||
{{(type==2||type==4)&&!is_next?'下一步':'添加到处方'}}
|
||||
</u-button>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-picker cancel-color="#6ACDBB" confirm-text="确定" :safe-area-inset-bottom="true" v-model="pickerShow"
|
||||
@confirm="confirm" mode="selector" :default-selector="[0]" range-key="name" :range="range[key]"
|
||||
@cancel="key='';pickerShow=false" :mask-close-able="false" :z-index="10079"></u-picker>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mixin from '../mixin/mixin.js';
|
||||
import {
|
||||
useTime,
|
||||
useType,
|
||||
useFre,
|
||||
westUnit,
|
||||
useNum,
|
||||
dosage,
|
||||
addOneChinese,
|
||||
addOneGranular
|
||||
} from '@/api/all.js'
|
||||
export default {
|
||||
name: 'add-setUsage',
|
||||
mixins: [mixin],
|
||||
props: {
|
||||
is_set: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
idx: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
info: {
|
||||
type: [Object, Array],
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
recipes: {
|
||||
type: [Array],
|
||||
default: () => {
|
||||
return [{
|
||||
id: 0,
|
||||
name: '煎煮'
|
||||
}, {
|
||||
id: 1,
|
||||
name: '外配'
|
||||
}]
|
||||
}
|
||||
},
|
||||
recipe: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
navbarHeight: 0,
|
||||
cm_id: [],
|
||||
is_next: false,
|
||||
pickerShow: false,
|
||||
obj: JSON.parse(JSON.stringify(this.info)),
|
||||
addRecipe: this.recipe,
|
||||
show: true,
|
||||
canvas: '',
|
||||
loading: false,
|
||||
key: '',
|
||||
range: {
|
||||
time: [],
|
||||
dosage: [],
|
||||
frequency: [],
|
||||
usage: [],
|
||||
describe: [],
|
||||
nums: []
|
||||
},
|
||||
see_rate: uni.getStorageSync('see_rate')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.navbarHeight = this.$navbarHeight
|
||||
this.useTime()
|
||||
this.useType()
|
||||
if (this.type == 1) {
|
||||
this.useFre()
|
||||
this.westUnit()
|
||||
} else {
|
||||
this.useNum()
|
||||
this.dosage()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getRate() {
|
||||
return ((Number((this.obj.price - this.obj.buy_price) / this.obj.buy_price)).toFixed(2)) * 100
|
||||
},
|
||||
next() {
|
||||
this.cm_id = new Array(this.obj.length)
|
||||
let grammage = this.obj.reduce((total, item) => {
|
||||
total += item['grammage'];
|
||||
return total
|
||||
}, 0) || 0
|
||||
if (grammage <= 0) {
|
||||
this.$toast('请设置克数');
|
||||
return
|
||||
}
|
||||
let index = 0;
|
||||
this.loading = true
|
||||
for (let i = 0; i < this.obj.length; i++) {
|
||||
|
||||
this.type == 2 && addOneChinese({
|
||||
drug_id: this.obj[i]['drug_id'],
|
||||
number: this.obj[i]['grammage'],
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then(res => {
|
||||
if (res.errcode == 0) {
|
||||
this.cm_id[i] = res.data[0]
|
||||
index++;
|
||||
this.is_next = index >= this.obj.length
|
||||
this.loading = !this.is_next
|
||||
} else {
|
||||
this.loading = false
|
||||
this.$toast(res.msg);
|
||||
}
|
||||
})
|
||||
this.type == 3 && addOneGranular({
|
||||
drug_id: this.obj[i]['drug_id'],
|
||||
number: this.obj[i]['grammage'],
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then(res => {
|
||||
console.log(res, 'addgramm');
|
||||
if (res.errcode == 0) {
|
||||
this.cm_id[i] = res.data[0]
|
||||
index++;
|
||||
this.is_next = index >= this.obj.length
|
||||
this.loading = !this.is_next
|
||||
} else {
|
||||
this.loading = false
|
||||
this.$toast(res.msg);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
dosage() {
|
||||
dosage({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['dosage'] = res.data.map(item => {
|
||||
return {
|
||||
...item,
|
||||
name: item.num + ""
|
||||
}
|
||||
})
|
||||
for (var i = 0; i < this.range['dosage'].length; i++) {
|
||||
if (this.range['dosage'][i].id == this.addRecipe['dosage']) {
|
||||
this.addRecipe['dosage'] = this.range['dosage'][i]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
useNum() {
|
||||
useNum({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['nums'] = res.data;
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
if (res.data[i].id == this.addRecipe['consumption']) {
|
||||
this.addRecipe['nums'] = res.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
westUnit() {
|
||||
westUnit({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['describe'] = res.data;
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
if (res.data[i].id == this.obj['unit_id']) {
|
||||
this.obj['describe'] = res.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
useTime() {
|
||||
useTime({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['time'] = res.data
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
if (this.type == 1) {
|
||||
if (res.data[i].id == this.obj['time_id']) {
|
||||
!this.obj.usages && (this.obj['usages'] = {})
|
||||
this.obj['usages'][0] = res.data[i]
|
||||
}
|
||||
} else {
|
||||
if (res.data[i].id == this.addRecipe['usage']) {
|
||||
this.addRecipe['usage'] = res.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
useType() {
|
||||
useType({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['usage'] = res.data
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
if (this.type == 1) {
|
||||
if (res.data[i].id == this.obj['type_id']) {
|
||||
!this.obj.usages && (this.obj['usages'] = {})
|
||||
this.obj['usages'][1] = res.data[i]
|
||||
}
|
||||
} else {
|
||||
if (res.data[i].id == this.addRecipe['fufa_id']) {
|
||||
this.addRecipe['time'] = res.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
useFre() {
|
||||
useFre({
|
||||
store_id: uni.getStorageSync('store_id') || '11001'
|
||||
}).then((res) => {
|
||||
if (res.errcode == 0) {
|
||||
this.range['frequency'] = res.data
|
||||
for (var i = 0; i < res.data.length; i++) {
|
||||
if (res.data[i].id == this.obj['frequency_id']) {
|
||||
this.obj['frequency'] = res.data[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
confirm(e) {
|
||||
switch (this.type) {
|
||||
case 1:
|
||||
if (this.key == 'time' || this.key == 'usage') {
|
||||
let i = this.key == 'time' ? 0 : 1;
|
||||
!this.obj.usages && (this.obj['usages'] = {})
|
||||
this.obj.usages[i] = this.range[this.key][e[0]];
|
||||
} else {
|
||||
this.obj[this.key] = this.range[this.key][e[0]]
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
this.addRecipe[this.key] = this.range[this.key][e[0]]
|
||||
break;
|
||||
case 3:
|
||||
this.addRecipe[this.key] = this.range[this.key][e[0]]
|
||||
break;
|
||||
}
|
||||
this.key = '';
|
||||
},
|
||||
add() {
|
||||
let obj = this.obj;
|
||||
let addRecipe = this.addRecipe;
|
||||
let data = {}
|
||||
let instruction = '';
|
||||
switch (this.type) {
|
||||
case 1:
|
||||
if (!obj['usages'] || (obj['usages'] && !obj['usages'][0] || !obj['usages'][1])) {
|
||||
this.$toast('请设置用法');
|
||||
return
|
||||
}
|
||||
if (!obj['describe']) {
|
||||
this.$toast('请设置每次');
|
||||
return
|
||||
}
|
||||
if (!obj['frequency']) {
|
||||
this.$toast('请设置频率');
|
||||
return
|
||||
}
|
||||
instruction = obj.usages['0']['name'] + obj.usages['1']['name'] + ',' + obj['frequency'][
|
||||
'name'
|
||||
] + ',每次' + (obj['count'] || 1)
|
||||
instruction += obj['describe']['name']
|
||||
data = {
|
||||
store_id: uni.getStorageSync('store_id') || 11001,
|
||||
content: obj['id'],
|
||||
number: obj['number'] || 1,
|
||||
usage: instruction,
|
||||
instruction,
|
||||
time_id: obj.usages['0']['id'],
|
||||
type_id: obj.usages['1']['id'],
|
||||
grain_number: obj['count'] || 1,
|
||||
count: obj['count'] || 1,
|
||||
available_days: 5,
|
||||
f_id: obj['frequency']['id'],
|
||||
wu_id: obj['describe']['id']
|
||||
}
|
||||
this.is_set ? this.setDrug({
|
||||
...data,
|
||||
pId: obj.pId
|
||||
}, obj) : this.addDrug(data, obj)
|
||||
break;
|
||||
case 2:
|
||||
if (!addRecipe.dosage) {
|
||||
this.$toast('请设置剂数');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.nums) {
|
||||
this.$toast('请设置用量');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.usage) {
|
||||
this.$toast('请设置用法');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.time) {
|
||||
this.$toast('请设置服法');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.remark) {
|
||||
this.$toast('请输入备注');
|
||||
return
|
||||
}
|
||||
let gram = obj.reduce((g, item) => {
|
||||
g += item['grammage']
|
||||
return g
|
||||
}, 0)
|
||||
data = {
|
||||
store_id: uni.getStorageSync('store_id') || 11001,
|
||||
deployment: addRecipe.deploy ? 2 : 1,
|
||||
dosage: addRecipe.dosage['id'],
|
||||
consumption: addRecipe.nums['id'],
|
||||
usage: addRecipe.usage['id'],
|
||||
fufa_id: addRecipe.time['id'],
|
||||
remark: addRecipe.remark,
|
||||
is_deepfry: addRecipe.frying ? 1 : 0,
|
||||
cm_id: this.cm_id.join(),
|
||||
directions: addRecipe.deploy ? '外配' : '煎煮' + addRecipe.nums['name'] + ',共' + addRecipe
|
||||
.dosage['name'] + '剂' + Number(addRecipe.dosage['name']) * gram + '克,每剂' + obj.length +
|
||||
'味' + gram + '克'
|
||||
}
|
||||
this.is_set ? this.setDrug({
|
||||
...data,
|
||||
pId: addRecipe.pId
|
||||
}, obj) : this.addDrug(data, obj, this.idx)
|
||||
break;
|
||||
case 3:
|
||||
// instruction = obj.usages['0']['name'] + obj.usages['1']['name'] + ',' + obj['frequency'][
|
||||
// 'name'
|
||||
// ] + ',每次' + (obj['count'] || 1)
|
||||
// instruction += obj['describe']['name']
|
||||
data = {
|
||||
store_id: uni.getStorageSync('store_id') || 11001,
|
||||
content: obj['id'],
|
||||
number: obj['number'] || 1,
|
||||
usage: instruction,
|
||||
instruction,
|
||||
time_id: obj.usages === undefined? '' : obj.usages['0']['id'],
|
||||
type_id: obj.usages === undefined? '' : obj.usages['1']['id'],
|
||||
grain_number: obj['count'] || 1,
|
||||
count: obj['count'] || 1,
|
||||
available_days: 5,
|
||||
f_id: obj['frequency'] !== undefined? obj['frequency']['id']: '',
|
||||
wu_id: obj['describe'] !== undefined? obj['describe']['id']: ''
|
||||
}
|
||||
// TODO 这里写加入逻辑
|
||||
this.is_set ? this.setDrug({
|
||||
...data,
|
||||
pId: obj.pId
|
||||
}, obj) : this.addDrug(data, obj)
|
||||
break;
|
||||
case 4:
|
||||
if (!addRecipe.dosage) {
|
||||
this.$toast('请设置剂数');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.nums) {
|
||||
this.$toast('请设置用量');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.usage) {
|
||||
this.$toast('请设置用法');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.time) {
|
||||
this.$toast('请设置服法');
|
||||
return
|
||||
}
|
||||
if (!addRecipe.remark) {
|
||||
this.$toast('请输入备注');
|
||||
return
|
||||
}
|
||||
let gram2 = obj.reduce((g, item) => {
|
||||
g += item['grammage']
|
||||
return g
|
||||
}, 0)
|
||||
data = {
|
||||
store_id: uni.getStorageSync('store_id') || 11001,
|
||||
deployment: addRecipe.deploy ? 2 : 1,
|
||||
dosage: addRecipe.dosage['id'],
|
||||
consumption: addRecipe.nums['id'],
|
||||
usage: addRecipe.usage['id'],
|
||||
fufa_id: addRecipe.time['id'],
|
||||
remark: addRecipe.remark,
|
||||
is_deepfry: addRecipe.frying ? 1 : 0,
|
||||
gm_id: this.cm_id.join(),
|
||||
directions: addRecipe.deploy ? '外配' : '煎煮' + addRecipe.nums['name'] + ',共' + addRecipe
|
||||
.dosage['name'] + '剂' + Number(addRecipe.dosage['name']) * gram2 + '克,每剂' + obj
|
||||
.length + '味' + gram2 + '克'
|
||||
}
|
||||
this.is_set ? this.setDrug({
|
||||
...data,
|
||||
pId: addRecipe.pId
|
||||
}, obj) : this.addDrug(data, obj, this.idx)
|
||||
break;
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
// 西药规格
|
||||
// 说明书
|
||||
previewImage(url) {
|
||||
// console.log(url,'aaaaaaaaaaaaa');
|
||||
let img=url.split(',')
|
||||
uni.previewImage({
|
||||
indicator: "none",
|
||||
current: img,
|
||||
urls: img,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.box {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: scroll;
|
||||
padding-bottom: 100rpx;
|
||||
padding-top: 40rpx;
|
||||
}
|
||||
|
||||
.rp {
|
||||
grid-column-gap: 64rpx;
|
||||
grid-row-gap: 16rpx;
|
||||
}
|
||||
|
||||
.add {
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
height: 110rpx;
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.minus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #F9FAFB;
|
||||
border-top-left-radius: 8rpx;
|
||||
border-bottom-left-radius: 8rpx;
|
||||
}
|
||||
|
||||
.plus {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background-color: #2979FF;
|
||||
border-top-right-radius: 8rpx;
|
||||
border-bottom-right-radius: 8rpx;
|
||||
}
|
||||
|
||||
.flexBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx !important;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-right: 100rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: rgba(0, 0, 0, 0.45)
|
||||
}
|
||||
|
||||
.val {
|
||||
flex: 1;
|
||||
color: rgba(0, 0, 0, 0.85)
|
||||
}
|
||||
}
|
||||
|
||||
.iBox {
|
||||
position: relative;
|
||||
padding-right: 100rpx;
|
||||
|
||||
.icon {
|
||||
width: 30rpx;
|
||||
height: 30rpx;
|
||||
position: absolute;
|
||||
right: 40rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<view class="box white" v-show="value">
|
||||
<scroll-view scroll-y="true" :style="{height: `calc(100vh - 122rpx - ${height}px - env(safe-area-inset-bottom))`}">
|
||||
<view class="p-32 flex-col">
|
||||
<view class="flex-row flex-jus-sp flex-ali-center m-b-48" v-for="item in list" :key="item.id">
|
||||
<u-icon :name="require('@/static/image/tag.png')" size="28" color="#6C7380" label-size="30" label-color="#6C7380"
|
||||
:label="item.name"></u-icon>
|
||||
<u-icon name="plus" size="32" color="#2979FF" v-if="!item.is_add" @click="$emit('add',item)">
|
||||
</u-icon>
|
||||
<d-text text="已添加" v-else></d-text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
diagnose: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
height: {
|
||||
type: [Number],
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.show = this.value
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.box{
|
||||
position: fixed;
|
||||
z-index: 999999999;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<view class="safe-area-inset-bottom notice p-2 ff_pfsc">
|
||||
<view class="p-col-16">
|
||||
<d-input prefixIcon="search" borderRadius="64rpx" v-model="keyword" type="text" placeholder="输入关键字搜索相关快速回复"
|
||||
@handleInput="toSearch" height="66"></d-input>
|
||||
</view>
|
||||
<view class="flex">
|
||||
<view class="tabs">
|
||||
<u-tabs name="txt" :list="tabs" :is-scroll="true" :current="active" @change="setTabActive"></u-tabs>
|
||||
</view>
|
||||
<view class="flex flex-ali-center">
|
||||
<u-icon label="管理" size="40" name="setting-fill"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
keyword: '',
|
||||
tabs: [{
|
||||
id: 1,
|
||||
txt: '测试1'
|
||||
}, {
|
||||
id: 2,
|
||||
txt: '测试2'
|
||||
}, {
|
||||
id: 3,
|
||||
txt: '测试3'
|
||||
}, {
|
||||
id: 4,
|
||||
txt: '测试4'
|
||||
}, {
|
||||
id: 5,
|
||||
txt: '测试5'
|
||||
}, {
|
||||
id: 6,
|
||||
txt: '测试6'
|
||||
}, {
|
||||
id: 7,
|
||||
txt: '测试7'
|
||||
}, {
|
||||
id: 8,
|
||||
txt: '测试8'
|
||||
}, ],
|
||||
active: 0,
|
||||
list: [],
|
||||
page: 1,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
getList() {
|
||||
this.list.push('获取列表')
|
||||
},
|
||||
setTabActive(index) {
|
||||
this.active = index
|
||||
this.page = 1
|
||||
this.getList()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyword(oldValue, newValue) {
|
||||
this.$u.debounce(this.getList, 500)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tabs{
|
||||
width: 590rpx;
|
||||
}
|
||||
</style>
|
||||
23
utils/workbenchCardPref.js
Normal file
23
utils/workbenchCardPref.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 工作台功能卡片形态偏好(长卡/短卡)
|
||||
* 存本地 storage(已确认不入库):用户在「我的」页切换后,各工作台首页 onShow 时读取生效
|
||||
* short = 双列短卡(默认);long = 整行长卡
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = 'wb_card_style'
|
||||
|
||||
/**
|
||||
* 读取卡片形态偏好,异常值一律回落 short,保证布局不会因脏数据坏掉
|
||||
*/
|
||||
export function getCardStyle() {
|
||||
const value = uni.getStorageSync(STORAGE_KEY)
|
||||
return value === 'long' ? 'long' : 'short'
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入卡片形态偏好
|
||||
* @param {'short'|'long'} style
|
||||
*/
|
||||
export function setCardStyle(style) {
|
||||
uni.setStorageSync(STORAGE_KEY, style === 'long' ? 'long' : 'short')
|
||||
}
|
||||
56
utils/workbenchEntries.js
Normal file
56
utils/workbenchEntries.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 小程序工作台功能入口下发数据的通用组装工具
|
||||
* 后端 /xxx-dashboard/entries 返回扁平数组:[{ code, name, description, icon, theme, path, group_name }]
|
||||
* 这里统一转换成四个工作台首页渲染用的 sections 结构:[{ key, title, items: [{ key, label, desc, icon, theme, path }] }]
|
||||
* 行为字段(角标/表单预填/弹窗等)由各首页按 code 通过 decorate 回调注入,DB 只管展示与有无
|
||||
*/
|
||||
|
||||
/** 未配置主题时的兜底配色(与各端硬编码菜单的占位灰一致) */
|
||||
const FALLBACK_THEME = { bg: 'linear-gradient(135deg, #F3F4F6, #E5E7EB)', color: '#6B7280' };
|
||||
|
||||
/**
|
||||
* 单条下发入口 → 渲染项归一化(首页 sections 与功能页常用/历史共用同一映射,保证字段一致)
|
||||
* @param {Object} entry 后端下发的入口对象
|
||||
* @returns {Object|null} { key, label, desc, icon, theme, path, group };非法入口返回 null
|
||||
*/
|
||||
export function normalizeEntry(entry) {
|
||||
if (!entry || !entry.code) return null;
|
||||
return {
|
||||
key: entry.code,
|
||||
label: entry.name || '',
|
||||
desc: entry.description || '',
|
||||
icon: entry.icon || 'grid-fill',
|
||||
theme: entry.theme && entry.theme.bg ? entry.theme : FALLBACK_THEME,
|
||||
path: entry.path || '',
|
||||
group: entry.group_name || '',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 下发入口 → sections 结构
|
||||
* @param {Array} entries 后端下发的入口数组(已按后台配置排序)
|
||||
* @param {Function} [decorate] 可选行为装饰回调 (item, entry) => item|null,返回 null 表示丢弃该项
|
||||
* @returns {Array} sections;entries 为空/非法时返回空数组(调用方据此回落硬编码菜单)
|
||||
*/
|
||||
export function groupEntriesToSections(entries, decorate) {
|
||||
if (!Array.isArray(entries) || entries.length === 0) return [];
|
||||
const sections = [];
|
||||
entries.forEach((entry) => {
|
||||
let item = normalizeEntry(entry);
|
||||
if (!item) return;
|
||||
if (typeof decorate === 'function') {
|
||||
item = decorate(item, entry);
|
||||
if (!item) return;
|
||||
}
|
||||
const title = entry.group_name || '快捷服务';
|
||||
let section = sections.find((s) => s.title === title);
|
||||
if (!section) {
|
||||
// key 用分组标题即可(仅作 v-for key,首个出现顺序即分组展示顺序)
|
||||
section = { key: title, title, items: [] };
|
||||
sections.push(section);
|
||||
}
|
||||
section.items.push(item);
|
||||
});
|
||||
// 丢弃被 decorate 全部过滤掉的空分组
|
||||
return sections.filter((s) => s.items.length > 0);
|
||||
}
|
||||
0
{{.Destination}}{{println}}{{end}}
Normal file
0
{{.Destination}}{{println}}{{end}}
Normal file
Reference in New Issue
Block a user