Files
李琦 cb290f3e95 fix(login): 密码登录补充密码非空前端校验
- doctor-login submitLogin 增加「请输入密码」前置校验,
  与 getCode 一致,避免空密码误触发登录请求
- 配合后端 service_user 空密码回退 PC 医生/药师密码校验
2026-07-19 16:08:23 +08:00

337 lines
9.8 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>