1. 更新切换账号逻辑
2. 诊所管理员功能上线
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -28,6 +28,7 @@
|
||||
|
||||
<!-- 操作面板(通栏) -->
|
||||
<view class="setting-panel">
|
||||
<account-switch-entry />
|
||||
<view
|
||||
class="setting-item action-item"
|
||||
hover-class="item-hover"
|
||||
@@ -46,9 +47,10 @@
|
||||
|
||||
<script>
|
||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout },
|
||||
components: { ClinicPageLayout, AccountSwitchEntry },
|
||||
data() {
|
||||
return {
|
||||
displayName: '',
|
||||
|
||||
@@ -1,135 +1,316 @@
|
||||
<template>
|
||||
<clinic-page-layout title="药品详情" :show-back="true">
|
||||
<view v-if="info.id" class="card">
|
||||
<image class="hero" :src="display.imageUrl" mode="aspectFit" />
|
||||
<view class="row"><text>药品名称</text><text>{{ display.drugName }}</text></view>
|
||||
<view class="row"><text>拼音</text><text>{{ display.pinyin }}</text></view>
|
||||
<view class="row"><text>供货价</text><text>{{ display.buyPrice }}</text></view>
|
||||
<view class="row"><text>建议售价</text><text>{{ display.suggestPrice }}</text></view>
|
||||
<view class="row"><text>售价</text><text>{{ display.price }}</text></view>
|
||||
<view class="row"><text>库存</text><text>{{ display.stock }}</text></view>
|
||||
<view class="row"><text>状态</text><text>{{ display.statusText }}</text></view>
|
||||
</view>
|
||||
<clinic-page-layout title="药品详情" :show-back="true">
|
||||
<view v-if="info.id" class="page-container">
|
||||
<!-- 头部信息卡片 -->
|
||||
<view class="card hero-card">
|
||||
<view class="image-wrapper">
|
||||
<image class="hero" :src="display.imageUrl" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="main-info">
|
||||
<text class="drug-name">{{ display.drugName }}</text>
|
||||
<text class="pinyin">拼音: {{ display.pinyin_simple || '--' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="info.id" class="btn-group">
|
||||
<button class="btn secondary" @click="openPriceEdit">修改售价</button>
|
||||
<button class="btn primary" @click="toggleStatus">
|
||||
{{ display.isOnShelf ? '下架' : '上架' }}
|
||||
</button>
|
||||
</view>
|
||||
<!-- 核心数据卡片 -->
|
||||
<view class="card data-card">
|
||||
<view class="row">
|
||||
<text class="label">状态</text>
|
||||
<view class="status-tag" :class="display.isOnShelf ? 'tag-on' : 'tag-off'">
|
||||
{{ display.statusText }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">库存</text>
|
||||
<text class="value stock-value">{{ display.stock }}</text>
|
||||
</view>
|
||||
|
||||
<price-edit-modal
|
||||
:visible="priceModalVisible"
|
||||
:item="info.id ? info : null"
|
||||
@close="closePriceEdit"
|
||||
@confirm="onPriceConfirm"
|
||||
/>
|
||||
</clinic-page-layout>
|
||||
<view class="divider"></view>
|
||||
|
||||
<view class="row">
|
||||
<text class="label">供货价</text>
|
||||
<text class="value">{{ display.buyPrice }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="label">建议售价</text>
|
||||
<text class="value">{{ display.suggestPrice }}</text>
|
||||
</view>
|
||||
<view class="row highlight-row">
|
||||
<text class="label">当前售价</text>
|
||||
<text class="value price-large">{{ display.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮组 -->
|
||||
<view v-if="info.id" class="action-group">
|
||||
<view class="btn-large btn-outline" hover-class="btn-hover-opacity" @click="openPriceEdit">
|
||||
修改售价
|
||||
</view>
|
||||
<view
|
||||
class="btn-large"
|
||||
:class="display.isOnShelf ? 'btn-danger' : 'btn-primary'"
|
||||
hover-class="btn-hover-opacity"
|
||||
@click="toggleStatus"
|
||||
>
|
||||
{{ display.isOnShelf ? '下架该药品' : '上架该药品' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<price-edit-modal
|
||||
:visible="priceModalVisible"
|
||||
:item="info.id ? info : null"
|
||||
@close="closePriceEdit"
|
||||
@confirm="onPriceConfirm"
|
||||
/>
|
||||
</clinic-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||
import PriceEditModal from './components/PriceEditModal.vue'
|
||||
import {
|
||||
getWarehouseDetail,
|
||||
updateWarehouseStatus,
|
||||
updateWarehousePrice,
|
||||
} from '@/api/clinicAdmin.js'
|
||||
import { mapWarehouseRow } from '../common/display.js'
|
||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||
import PriceEditModal from './components/PriceEditModal.vue'
|
||||
import {
|
||||
getWarehouseDetail,
|
||||
updateWarehouseStatus,
|
||||
updateWarehousePrice,
|
||||
} from '@/api/clinicAdmin.js'
|
||||
import { mapWarehouseRow } from '../common/display.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout, PriceEditModal },
|
||||
data() {
|
||||
return {
|
||||
id: 0,
|
||||
info: {},
|
||||
priceModalVisible: false,
|
||||
priceSubmitting: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
display() {
|
||||
return mapWarehouseRow(this.info)
|
||||
},
|
||||
},
|
||||
onLoad(q) {
|
||||
this.id = Number(q.id || 0)
|
||||
this.load()
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
getWarehouseDetail(this.id).then(w => {
|
||||
if (w.ok) this.info = w.data || {}
|
||||
})
|
||||
},
|
||||
openPriceEdit() {
|
||||
this.priceModalVisible = true
|
||||
},
|
||||
closePriceEdit() {
|
||||
this.priceModalVisible = false
|
||||
},
|
||||
onPriceConfirm(price) {
|
||||
if (this.priceSubmitting) return
|
||||
this.priceSubmitting = true
|
||||
updateWarehousePrice({ id: this.id, price }).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '修改成功' })
|
||||
this.closePriceEdit()
|
||||
this.load()
|
||||
}
|
||||
}).finally(() => { this.priceSubmitting = false })
|
||||
},
|
||||
toggleStatus() {
|
||||
updateWarehouseStatus(this.id).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '操作成功' })
|
||||
this.load()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
export default {
|
||||
components: { ClinicPageLayout, PriceEditModal },
|
||||
data() {
|
||||
return {
|
||||
id: 0,
|
||||
info: {},
|
||||
priceModalVisible: false,
|
||||
priceSubmitting: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
display() {
|
||||
return mapWarehouseRow(this.info)
|
||||
},
|
||||
},
|
||||
onLoad(q) {
|
||||
this.id = Number(q.id || 0)
|
||||
this.load()
|
||||
},
|
||||
methods: {
|
||||
load() {
|
||||
getWarehouseDetail(this.id).then(w => {
|
||||
if (w.ok) this.info = w.data || {}
|
||||
})
|
||||
},
|
||||
openPriceEdit() {
|
||||
this.priceModalVisible = true
|
||||
},
|
||||
closePriceEdit() {
|
||||
this.priceModalVisible = false
|
||||
},
|
||||
onPriceConfirm(price) {
|
||||
if (this.priceSubmitting) return
|
||||
this.priceSubmitting = true
|
||||
updateWarehousePrice({ id: this.id, price }).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '修改成功' })
|
||||
this.closePriceEdit()
|
||||
this.load()
|
||||
}
|
||||
}).finally(() => { this.priceSubmitting = false })
|
||||
},
|
||||
toggleStatus() {
|
||||
updateWarehouseStatus(this.id).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '操作成功' })
|
||||
this.load()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 同步了列表页的核心色板 */
|
||||
$theme-color: #6acdbb;
|
||||
$theme-color-light: rgba(106, 205, 187, 0.12);
|
||||
$text-primary: #2c3e50;
|
||||
$text-secondary: #8a97a3;
|
||||
$text-light: #b0b8c1;
|
||||
$bg-color: #f7f9fa;
|
||||
$border-color: #ebedf0;
|
||||
$danger-color: #ff6b6b;
|
||||
$danger-color-light: rgba(255, 107, 107, 0.1);
|
||||
|
||||
.page-container {
|
||||
padding: 24rpx;
|
||||
padding-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 32rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
/* 头部卡片特异样式 */
|
||||
.hero-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 40rpx;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
width: 100%;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
background: $bg-color;
|
||||
border: 1rpx solid $border-color;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.hero {
|
||||
width: 100%;
|
||||
height: 360rpx;
|
||||
margin-bottom: 24rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #f8f8f8;
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.main-info {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.drug-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
margin-bottom: 12rpx;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.pinyin {
|
||||
font-size: 26rpx;
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
/* 核心数据卡片样式 */
|
||||
.data-card {
|
||||
padding: 16rpx 32rpx;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 0;
|
||||
font-size: 28rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
.btn-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: $border-color;
|
||||
margin: 8rpx 0;
|
||||
}
|
||||
.btn {
|
||||
border-radius: 44rpx;
|
||||
font-size: 30rpx;
|
||||
margin: 0;
|
||||
|
||||
.label {
|
||||
font-size: 28rpx;
|
||||
color: $text-secondary;
|
||||
}
|
||||
.btn.primary {
|
||||
background: #6acdbb;
|
||||
color: #fff;
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: $text-primary;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn.secondary {
|
||||
background: #fff;
|
||||
color: #6acdbb;
|
||||
border: 1rpx solid #6acdbb;
|
||||
|
||||
.stock-value {
|
||||
font-family: monospace; /* 数字更清晰 */
|
||||
font-size: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
.highlight-row {
|
||||
margin-top: 8rpx;
|
||||
background: #fafbfa; /* 极浅的背景强调 */
|
||||
margin: 0 -32rpx;
|
||||
padding: 32rpx;
|
||||
border-bottom-left-radius: 24rpx;
|
||||
border-bottom-right-radius: 24rpx;
|
||||
}
|
||||
|
||||
.highlight-row .label {
|
||||
color: $text-primary;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.price-large {
|
||||
font-size: 40rpx;
|
||||
color: $theme-color;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
.status-tag {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
padding: 6rpx 20rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.tag-on {
|
||||
color: $theme-color;
|
||||
background: $theme-color-light;
|
||||
}
|
||||
|
||||
.tag-off {
|
||||
color: $text-secondary;
|
||||
background: $bg-color;
|
||||
}
|
||||
|
||||
/* 底部操作按钮组 */
|
||||
.action-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 48rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
transition: opacity 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: $theme-color;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 8rpx 24rpx $theme-color-light;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: $danger-color;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 8rpx 24rpx $danger-color-light;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: #ffffff;
|
||||
color: $theme-color;
|
||||
border: 2rpx solid $theme-color;
|
||||
}
|
||||
|
||||
.btn-hover-opacity {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
@@ -1,231 +1,383 @@
|
||||
<template>
|
||||
<clinic-page-layout :title="pageTitle" :show-back="true">
|
||||
<view class="search">
|
||||
<input v-model="keyword" placeholder="搜索药品名称" @confirm="reload" />
|
||||
<text @click="reload">搜索</text>
|
||||
</view>
|
||||
<clinic-page-layout :title="pageTitle" :show-back="true">
|
||||
<!-- 搜索栏优化:胶囊式设计,增加视觉呼吸感 -->
|
||||
<view class="search-wrapper">
|
||||
<view class="search-bar">
|
||||
<input class="search-input" v-model="keyword" placeholder="搜索药品名称" placeholder-class="search-placeholder" @confirm="reload" />
|
||||
<view class="search-btn" hover-class="btn-hover" @click="reload">搜索</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item._wxKey"
|
||||
class="item"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<image class="thumb" :src="rowDisplay(item).imageUrl" mode="aspectFill" />
|
||||
<view class="body">
|
||||
<view class="title">{{ rowDisplay(item).drugName }}</view>
|
||||
<view class="sub">供货 {{ rowDisplay(item).buyPrice }} · 售价 {{ rowDisplay(item).price }}</view>
|
||||
<view class="sub">库存 {{ rowDisplay(item).stock }}</view>
|
||||
</view>
|
||||
<view class="actions" @click.stop>
|
||||
<view class="tag" :class="rowDisplay(item).isOnShelf ? 'on' : 'off'">{{ rowDisplay(item).statusText }}</view>
|
||||
<view class="btn-row">
|
||||
<text class="act-btn" @click="openPriceEdit(item)">改价</text>
|
||||
<text class="act-btn shelf" @click="toggleStatus(item)">
|
||||
{{ rowDisplay(item).isOnShelf ? '下架' : '上架' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list-container">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item._wxKey"
|
||||
class="item-card"
|
||||
hover-class="card-hover"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<!-- 左侧缩略图 -->
|
||||
<image class="thumb" :src="rowDisplay(item).imageUrl" mode="aspectFill" />
|
||||
|
||||
<view v-if="!loading && !list.length" class="empty">暂无药品</view>
|
||||
<!-- 右侧内容区 -->
|
||||
<view class="content">
|
||||
<!-- 顶部:标题与状态标签对齐 -->
|
||||
<view class="header-row">
|
||||
<text class="title">{{ rowDisplay(item).drugName }}</text>
|
||||
<view class="status-tag" :class="rowDisplay(item).isOnShelf ? 'tag-on' : 'tag-off'">
|
||||
{{ rowDisplay(item).statusText }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<price-edit-modal
|
||||
:visible="priceModalVisible"
|
||||
:item="priceEditItem"
|
||||
@close="closePriceEdit"
|
||||
@confirm="onPriceConfirm"
|
||||
/>
|
||||
</clinic-page-layout>
|
||||
<!-- 中部:详细信息 -->
|
||||
<view class="info-group">
|
||||
<text class="info-text">供货 {{ rowDisplay(item).buyPrice }} · 售价 {{ rowDisplay(item).price }}</text>
|
||||
<text class="info-text">库存 {{ rowDisplay(item).stock }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部:操作按钮横向排列,增加点击区域与反馈 -->
|
||||
<view class="action-row" @click.stop>
|
||||
<view class="action-btn outline" hover-class="btn-hover-opacity" @click.stop="openPriceEdit(item)">
|
||||
改价
|
||||
</view>
|
||||
<view class="action-btn" :class="rowDisplay(item).isOnShelf ? 'btn-danger' : 'btn-primary'" hover-class="btn-hover-opacity" @click.stop="toggleStatus(item)">
|
||||
{{ rowDisplay(item).isOnShelf ? '下架' : '上架' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态优化 -->
|
||||
<view v-if="!loading && !list.length" class="empty-state">
|
||||
<view class="empty-icon"></view>
|
||||
<text class="empty-text">暂无药品数据</text>
|
||||
</view>
|
||||
|
||||
<price-edit-modal
|
||||
:visible="priceModalVisible"
|
||||
:item="priceEditItem"
|
||||
@close="closePriceEdit"
|
||||
@confirm="onPriceConfirm"
|
||||
/>
|
||||
</clinic-page-layout>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||
import PriceEditModal from './components/PriceEditModal.vue'
|
||||
import {
|
||||
getWarehouseList,
|
||||
updateWarehouseStatus,
|
||||
updateWarehousePrice,
|
||||
} from '@/api/clinicAdmin.js'
|
||||
import { withWxKey } from '@/utils/wxListKey.js'
|
||||
import { mapWarehouseRow } from '../common/display.js'
|
||||
import ClinicPageLayout from '../components/clinic-page-layout.vue'
|
||||
import PriceEditModal from './components/PriceEditModal.vue'
|
||||
import {
|
||||
getWarehouseList,
|
||||
updateWarehouseStatus,
|
||||
updateWarehousePrice,
|
||||
} from '@/api/clinicAdmin.js'
|
||||
import { withWxKey } from '@/utils/wxListKey.js'
|
||||
import { mapWarehouseRow } from '../common/display.js'
|
||||
|
||||
export default {
|
||||
components: { ClinicPageLayout, PriceEditModal },
|
||||
data() {
|
||||
return {
|
||||
pageTitle: '仓库管理',
|
||||
warehouseType: '',
|
||||
keyword: '',
|
||||
list: [],
|
||||
page: 1,
|
||||
loading: false,
|
||||
priceModalVisible: false,
|
||||
priceEditItem: null,
|
||||
priceSubmitting: false,
|
||||
}
|
||||
},
|
||||
onLoad(q) {
|
||||
if (q.label) {
|
||||
this.pageTitle = decodeURIComponent(q.label)
|
||||
}
|
||||
if (q.type != null && q.type !== '') {
|
||||
this.warehouseType = q.type
|
||||
}
|
||||
this.reload()
|
||||
},
|
||||
onReachBottom() {
|
||||
this.load(this.page + 1, true)
|
||||
},
|
||||
methods: {
|
||||
rowDisplay(item) {
|
||||
return mapWarehouseRow(item)
|
||||
},
|
||||
reload() {
|
||||
this.page = 1
|
||||
this.load(1, false)
|
||||
},
|
||||
load(page, append) {
|
||||
this.loading = true
|
||||
const params = { page, pageSize: 20 }
|
||||
if (this.keyword) params.name = this.keyword
|
||||
if (this.warehouseType !== '' && this.warehouseType != null) {
|
||||
params.type = this.warehouseType
|
||||
}
|
||||
getWarehouseList(params).then(w => {
|
||||
const items = withWxKey((w.data && w.data.items) || [], 'id', 'wh')
|
||||
this.list = append ? this.list.concat(items) : items
|
||||
this.page = page
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
goDetail(item) {
|
||||
uni.navigateTo({ url: '/subPackages/sub_clinic_admin/warehouse/detail?id=' + item.id })
|
||||
},
|
||||
openPriceEdit(item) {
|
||||
this.priceEditItem = item
|
||||
this.priceModalVisible = true
|
||||
},
|
||||
closePriceEdit() {
|
||||
this.priceModalVisible = false
|
||||
this.priceEditItem = null
|
||||
},
|
||||
onPriceConfirm(price) {
|
||||
if (!this.priceEditItem || this.priceSubmitting) return
|
||||
this.priceSubmitting = true
|
||||
updateWarehousePrice({ id: this.priceEditItem.id, price }).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '修改成功' })
|
||||
this.closePriceEdit()
|
||||
this.reload()
|
||||
}
|
||||
}).finally(() => { this.priceSubmitting = false })
|
||||
},
|
||||
toggleStatus(item) {
|
||||
const action = item.status === 2 ? '下架' : '上架'
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定' + action + '「' + ((item.drug && item.drug.drug_name) || '') + '」?',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
updateWarehouseStatus(item.id).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '操作成功' })
|
||||
item.status = item.status === 2 ? 1 : 2
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
export default {
|
||||
components: { ClinicPageLayout, PriceEditModal },
|
||||
data() {
|
||||
return {
|
||||
pageTitle: '仓库管理',
|
||||
warehouseType: '',
|
||||
keyword: '',
|
||||
list: [],
|
||||
page: 1,
|
||||
loading: false,
|
||||
priceModalVisible: false,
|
||||
priceEditItem: null,
|
||||
priceSubmitting: false,
|
||||
}
|
||||
},
|
||||
onLoad(q) {
|
||||
if (q.label) {
|
||||
this.pageTitle = decodeURIComponent(q.label)
|
||||
}
|
||||
if (q.type != null && q.type !== '') {
|
||||
this.warehouseType = q.type
|
||||
}
|
||||
this.reload()
|
||||
},
|
||||
onReachBottom() {
|
||||
this.load(this.page + 1, true)
|
||||
},
|
||||
methods: {
|
||||
rowDisplay(item) {
|
||||
return mapWarehouseRow(item)
|
||||
},
|
||||
reload() {
|
||||
this.page = 1
|
||||
this.load(1, false)
|
||||
},
|
||||
load(page, append) {
|
||||
this.loading = true
|
||||
const params = { page, pageSize: 20 }
|
||||
if (this.keyword) params.name = this.keyword
|
||||
if (this.warehouseType !== '' && this.warehouseType != null) {
|
||||
params.type = this.warehouseType
|
||||
}
|
||||
getWarehouseList(params).then(w => {
|
||||
const items = withWxKey((w.data && w.data.items) || [], 'id', 'wh')
|
||||
this.list = append ? this.list.concat(items) : items
|
||||
this.page = page
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
goDetail(item) {
|
||||
uni.navigateTo({ url: '/subPackages/sub_clinic_admin/warehouse/detail?id=' + item.id })
|
||||
},
|
||||
openPriceEdit(item) {
|
||||
this.priceEditItem = item
|
||||
this.priceModalVisible = true
|
||||
},
|
||||
closePriceEdit() {
|
||||
this.priceModalVisible = false
|
||||
this.priceEditItem = null
|
||||
},
|
||||
onPriceConfirm(price) {
|
||||
if (!this.priceEditItem || this.priceSubmitting) return
|
||||
this.priceSubmitting = true
|
||||
updateWarehousePrice({ id: this.priceEditItem.id, price }).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '修改成功' })
|
||||
this.closePriceEdit()
|
||||
this.reload()
|
||||
}
|
||||
}).finally(() => { this.priceSubmitting = false })
|
||||
},
|
||||
toggleStatus(item) {
|
||||
const action = item.status === 2 ? '下架' : '上架'
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定' + action + '「' + ((item.drug && item.drug.drug_name) || '') + '」?',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
updateWarehouseStatus(item.id).then(w => {
|
||||
if (w.ok) {
|
||||
uni.showToast({ title: '操作成功' })
|
||||
item.status = item.status === 2 ? 1 : 2
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
align-items: center;
|
||||
/* 核心主题色变量提取,方便后续统一管理 */
|
||||
$theme-color: #6acdbb;
|
||||
$theme-color-light: rgba(106, 205, 187, 0.12);
|
||||
$text-primary: #2c3e50;
|
||||
$text-secondary: #8a97a3;
|
||||
$text-light: #b0b8c1;
|
||||
$bg-color: #f7f9fa;
|
||||
$border-color: #ebedf0;
|
||||
$danger-color: #ff6b6b;
|
||||
$danger-color-light: rgba(255, 107, 107, 0.1);
|
||||
|
||||
.search-wrapper {
|
||||
padding: 16rpx 24rpx;
|
||||
background-color: transparent;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
.search input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #ffffff;
|
||||
padding: 12rpx 16rpx 12rpx 32rpx;
|
||||
border-radius: 40rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.search text {
|
||||
color: #6acdbb;
|
||||
font-size: 28rpx;
|
||||
margin-left: 16rpx;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: $text-primary;
|
||||
height: 60rpx;
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
||||
|
||||
.search-placeholder {
|
||||
color: $text-light;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
background: $theme-color;
|
||||
color: #ffffff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
padding: 12rpx 36rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-left: 16rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.list-container {
|
||||
padding: 0 24rpx 24rpx;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
display: flex;
|
||||
background: #ffffff;
|
||||
padding: 24rpx;
|
||||
border-radius: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
transform: scale(0.99);
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 16rpx;
|
||||
flex-shrink: 0;
|
||||
background: $bg-color;
|
||||
border: 1rpx solid $border-color;
|
||||
}
|
||||
.body {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
min-width: 0;
|
||||
padding-right: 12rpx;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-size: 30rpx;
|
||||
color: $text-primary;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
flex: 1;
|
||||
margin-right: 16rpx;
|
||||
/* 单行截断 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sub {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
|
||||
.status-tag {
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 8rpx;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.actions {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 12rpx;
|
||||
|
||||
.tag-on {
|
||||
color: $theme-color;
|
||||
background: $theme-color-light;
|
||||
}
|
||||
.tag {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.tag-off {
|
||||
color: $text-secondary;
|
||||
background: $bg-color;
|
||||
}
|
||||
.tag.on {
|
||||
color: #6acdbb;
|
||||
background: rgba(106, 205, 187, 0.12);
|
||||
|
||||
.info-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.tag.off {
|
||||
color: #999;
|
||||
background: #f5f5f5;
|
||||
|
||||
.info-text {
|
||||
font-size: 24rpx;
|
||||
color: $text-secondary;
|
||||
}
|
||||
.btn-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: flex-end;
|
||||
|
||||
.action-row {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-top: auto;
|
||||
}
|
||||
.act-btn {
|
||||
font-size: 24rpx;
|
||||
color: #6acdbb;
|
||||
padding: 4rpx 0;
|
||||
|
||||
.action-btn {
|
||||
font-size: 24rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
border-radius: 12rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.act-btn.shelf {
|
||||
color: #666;
|
||||
|
||||
.action-btn.outline {
|
||||
color: $text-primary;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid $border-color;
|
||||
}
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 60rpx;
|
||||
|
||||
.action-btn.btn-primary {
|
||||
color: #ffffff;
|
||||
background: $theme-color;
|
||||
border: 1rpx solid $theme-color;
|
||||
}
|
||||
</style>
|
||||
|
||||
.action-btn.btn-danger {
|
||||
color: $danger-color;
|
||||
background: $danger-color-light;
|
||||
border: 1rpx solid transparent;
|
||||
}
|
||||
|
||||
.btn-hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-hover-opacity {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
background-color: $bg-color;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 30rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 用伪元素做一个简单的空状态图形占位,有条件可替换为真实的Image/SVG */
|
||||
.empty-icon::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border: 8rpx solid $border-color;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
color: $text-light;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
</style>
|
||||
@@ -36,6 +36,10 @@
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<view class="p-row-32 m-t-8 white b-r-8">
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
|
||||
<view class="p-row-32 m-t-8" style="height: 100rpx;">
|
||||
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'96rpx'}" @click="logout">
|
||||
退出登录</u-button>
|
||||
@@ -49,8 +53,10 @@
|
||||
logout,
|
||||
toUpload,
|
||||
} from '@/api/all.js'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
import { chooseAvatarImage } from '@/common/js/select-avatar-image.js';
|
||||
export default {
|
||||
components: { AccountSwitchEntry },
|
||||
data() {
|
||||
return {
|
||||
doctorInfo: []
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
40056256325
|
||||
</view>
|
||||
</view>
|
||||
<view class="p-row-32 m-t-8 white">
|
||||
<account-switch-entry />
|
||||
</view>
|
||||
<view class="p-row-32 m-t-8" style="height: 80rpx;">
|
||||
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'86rpx'}" @click="logout">
|
||||
退出登录</u-button>
|
||||
@@ -33,7 +36,9 @@
|
||||
import {
|
||||
logout
|
||||
} from '@/api/all.js'
|
||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||
export default {
|
||||
components: { AccountSwitchEntry },
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user