- doctor-login submitLogin 增加「请输入密码」前置校验, 与 getCode 一致,避免空密码误触发登录请求 - 配合后端 service_user 空密码回退 PC 医生/药师密码校验
337 lines
9.8 KiB
Vue
337 lines
9.8 KiB
Vue
<template>
|
||
<clinic-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">
|
||
<view class="badge"><text class="badge-text">诊所管理系统</text></view>
|
||
<text class="store-name">{{ storeName }}</text>
|
||
<text class="store-sub">欢迎回来,今天也要高效工作</text>
|
||
</view>
|
||
|
||
<!-- 诊所专属二维码 -->
|
||
<view class="qr-container" @click="openStoreQrCode">
|
||
<image
|
||
v-if="storeQrCode"
|
||
class="qr-image"
|
||
:src="storeQrCode"
|
||
mode="aspectFit"
|
||
/>
|
||
<view v-else class="qr-placeholder">
|
||
<u-icon name="scan" size="48" color="#666"></u-icon>
|
||
</view>
|
||
<text class="qr-hint">诊所名片</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>
|
||
</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>
|
||
</view>
|
||
</view>
|
||
</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 { 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',
|
||
}
|
||
|
||
export default {
|
||
components: { ClinicPageLayout },
|
||
data() {
|
||
return {
|
||
storeName: '',
|
||
storeQrCode: '',
|
||
storeQrData: null,
|
||
warehouseMenus: [WAREHOUSE_FALLBACK],
|
||
}
|
||
},
|
||
computed: {
|
||
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 },
|
||
]
|
||
},
|
||
},
|
||
onShow() {
|
||
const user = uni.getStorageSync('clinic_admin_user') || {}
|
||
this.storeName = user.store_name || (user.store && user.store.name) || user.nick_name || '诊所管理'
|
||
this.loadStoreQr()
|
||
this.loadWarehouseMenus()
|
||
},
|
||
methods: {
|
||
async loadStoreQr() {
|
||
this.storeQrCode = ''
|
||
this.storeQrData = null
|
||
const wrap = await getClinicAdminStoreQr()
|
||
if (!wrap || !wrap.ok) return
|
||
const store = wrap.data || {}
|
||
if (!store.qr_code) return
|
||
this.storeQrData = store
|
||
this.storeQrCode = store.qr_code
|
||
if (store.name) this.storeName = store.name
|
||
},
|
||
openStoreQrCode() {
|
||
if (!this.storeQrCode || !this.storeQrData) {
|
||
uni.showToast({ title: '诊所尚未生成二维码', icon: 'none' })
|
||
return
|
||
}
|
||
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>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 全局容器背景:更干净的高级浅灰底色 */
|
||
.workspace-container {
|
||
padding-bottom: 80rpx;
|
||
background-color: #F4F6F8;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ================= 极简顶部英雄区 ================= */
|
||
.hero-section {
|
||
background: linear-gradient(135deg, #6acdbb 0%, #4db6a8 100%);
|
||
padding: 40rpx 40rpx 60rpx;
|
||
border-bottom-left-radius: 48rpx;
|
||
border-bottom-right-radius: 48rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(77, 182, 168, 0.2);
|
||
}
|
||
|
||
.brand-info-wrap {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.brand-text {
|
||
flex: 1;
|
||
}
|
||
|
||
.badge {
|
||
display: inline-flex;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
backdrop-filter: blur(8px);
|
||
padding: 6rpx 20rpx;
|
||
border-radius: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.badge-text {
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
letter-spacing: 2rpx;
|
||
}
|
||
|
||
.store-name {
|
||
font-size: 42rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
display: block;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.store-sub {
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.85);
|
||
display: block;
|
||
}
|
||
|
||
/* 二维码容器样式 */
|
||
.qr-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.qr-image, .qr-placeholder {
|
||
width: 110rpx;
|
||
height: 110rpx;
|
||
background: #fff;
|
||
border-radius: 24rpx;
|
||
padding: 10rpx;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.qr-hint {
|
||
font-size: 20rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
margin-top: 12rpx;
|
||
background: rgba(0, 0, 0, 0.1);
|
||
padding: 4rpx 16rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
|
||
/* ================= 主体内容区:快捷服务 ================= */
|
||
.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> |