feat: 小程序首页入口icon优化
This commit is contained in:
@@ -69,7 +69,8 @@ export const modalFormProps: VbenFormProps = {
|
||||
{
|
||||
component: 'Avatar',
|
||||
fieldName: 'icon',
|
||||
label: '图标',
|
||||
label: '入口图',
|
||||
help: '患者端首页专区入口图,建议透明底。未上传时用默认矢量图。',
|
||||
defaultValue: '',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,27 +1,64 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 系统配置 - 患者端小程序 / 首页展示:金刚区布局 + 订单提醒样式
|
||||
* 系统配置 - 患者端小程序 / 首页展示:待办入口排布 + 入口图 + 订单待办样式
|
||||
* 排布作用在待支付挂号/商品、已发货、待确认收货,不新增药店专区
|
||||
*/
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
|
||||
import { Button, message, Radio } from 'ant-design-vue';
|
||||
|
||||
import FormAvatar from '#/components/form/components/avatar.vue';
|
||||
|
||||
import { getSystemConfigByKeys, saveSystemConfig } from '../api';
|
||||
|
||||
defineOptions({ name: 'HomeDisplayConfigPanel' });
|
||||
|
||||
const REMIND_ICON_FIELDS = [
|
||||
{
|
||||
key: 'unpaid_register',
|
||||
configKey: 'home_remind_icon_unpaid_register',
|
||||
label: '待支付挂号',
|
||||
sort: 82,
|
||||
},
|
||||
{
|
||||
key: 'unpaid_product',
|
||||
configKey: 'home_remind_icon_unpaid_product',
|
||||
label: '待支付商品',
|
||||
sort: 83,
|
||||
},
|
||||
{
|
||||
key: 'shipped',
|
||||
configKey: 'home_remind_icon_shipped',
|
||||
label: '已发货',
|
||||
sort: 84,
|
||||
},
|
||||
{
|
||||
key: 'arrived',
|
||||
configKey: 'home_remind_icon_arrived',
|
||||
label: '待确认收货',
|
||||
sort: 85,
|
||||
},
|
||||
] as const;
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const zoneLayout = ref<'scroll' | 'wrap2' | 'wrap'>('scroll');
|
||||
const orderRemindStyle = ref<'grid' | 'zone'>('grid');
|
||||
const remindIcons = reactive<Record<string, string>>({
|
||||
unpaid_register: '',
|
||||
unpaid_product: '',
|
||||
shipped: '',
|
||||
arrived: '',
|
||||
});
|
||||
|
||||
/** 读取首页展示样式,缺省回落默认值 */
|
||||
/** 读取排布、样式和四张待办入口图 */
|
||||
async function load() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getSystemConfigByKeys([
|
||||
'home_zone_layout',
|
||||
'home_order_remind_style',
|
||||
...REMIND_ICON_FIELDS.map((item) => item.configKey),
|
||||
]);
|
||||
const data = res && typeof res === 'object' ? res : {};
|
||||
const layout = String(data.home_zone_layout || 'scroll');
|
||||
@@ -29,6 +66,9 @@ async function load() {
|
||||
layout === 'wrap' || layout === 'wrap2' ? layout : 'scroll';
|
||||
orderRemindStyle.value =
|
||||
String(data.home_order_remind_style || '') === 'zone' ? 'zone' : 'grid';
|
||||
for (const item of REMIND_ICON_FIELDS) {
|
||||
remindIcons[item.key] = String(data[item.configKey] || '');
|
||||
}
|
||||
} catch {
|
||||
zoneLayout.value = 'scroll';
|
||||
orderRemindStyle.value = 'grid';
|
||||
@@ -37,7 +77,7 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 写入系统配置,患者端首页即时读取 */
|
||||
/** 写入系统配置,患者端首页即时读取入口图和排布 */
|
||||
async function handleSave() {
|
||||
saving.value = true;
|
||||
try {
|
||||
@@ -47,7 +87,8 @@ async function handleSave() {
|
||||
config_value: zoneLayout.value,
|
||||
value_type: 'string',
|
||||
config_group: 'home',
|
||||
description: '首页金刚区布局:scroll横向滑 / wrap换行 / wrap2一行两个',
|
||||
description:
|
||||
'首页待办入口排布:scroll横向滑 / wrap一行五个 / wrap2一行两个',
|
||||
sort: 80,
|
||||
},
|
||||
{
|
||||
@@ -55,9 +96,17 @@ async function handleSave() {
|
||||
config_value: orderRemindStyle.value,
|
||||
value_type: 'string',
|
||||
config_group: 'home',
|
||||
description: '首页订单提醒样式:grid一行两列卡片 / zone金刚区图标',
|
||||
description: '首页订单待办样式:grid一行两列卡片 / zone图标入口',
|
||||
sort: 81,
|
||||
},
|
||||
...REMIND_ICON_FIELDS.map((item) => ({
|
||||
config_key: item.configKey,
|
||||
config_value: remindIcons[item.key] || '',
|
||||
value_type: 'string',
|
||||
config_group: 'home',
|
||||
description: `首页待办入口图:${item.label}`,
|
||||
sort: item.sort,
|
||||
})),
|
||||
]);
|
||||
message.success('保存成功');
|
||||
} finally {
|
||||
@@ -72,23 +121,88 @@ onMounted(load);
|
||||
<div class="cfg-panel" :class="{ 'opacity-60': loading }">
|
||||
<div class="cfg-panel-body">
|
||||
<div class="mb-2 font-medium text-[hsl(var(--foreground))]">
|
||||
金刚区布局
|
||||
待办入口排布
|
||||
</div>
|
||||
<div class="mb-2 text-sm text-[hsl(var(--muted-foreground))]">
|
||||
控制患者端首页专区入口的排列方式。
|
||||
<div class="mb-3 text-sm text-[hsl(var(--muted-foreground))]">
|
||||
控制首页待支付挂号、待支付商品、已发货、待确认收货的排列方式。选「图标入口」时生效;药店专区入口也沿用同一排布。
|
||||
</div>
|
||||
<div class="layout-pick">
|
||||
<button
|
||||
type="button"
|
||||
class="layout-pick-card"
|
||||
:class="{ 'is-active': zoneLayout === 'scroll' }"
|
||||
@click="zoneLayout = 'scroll'"
|
||||
>
|
||||
<div class="layout-mock layout-mock--scroll">
|
||||
<i class="dot dot-a"></i>
|
||||
<i class="dot dot-b"></i>
|
||||
<i class="dot dot-c"></i>
|
||||
<i class="dot dot-d"></i>
|
||||
<i class="dot dot-fade"></i>
|
||||
</div>
|
||||
<div class="layout-pick-title">横向滑动</div>
|
||||
<div class="layout-pick-desc">一排图标,左右滑看更多</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="layout-pick-card"
|
||||
:class="{ 'is-active': zoneLayout === 'wrap' }"
|
||||
@click="zoneLayout = 'wrap'"
|
||||
>
|
||||
<div class="layout-mock layout-mock--wrap">
|
||||
<i class="dot dot-a"></i>
|
||||
<i class="dot dot-b"></i>
|
||||
<i class="dot dot-c"></i>
|
||||
<i class="dot dot-d"></i>
|
||||
<i class="dot dot-empty"></i>
|
||||
</div>
|
||||
<div class="layout-pick-title">换行(一行五个)</div>
|
||||
<div class="layout-pick-desc">五个一排,多了自动换行</div>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="layout-pick-card"
|
||||
:class="{ 'is-active': zoneLayout === 'wrap2' }"
|
||||
@click="zoneLayout = 'wrap2'"
|
||||
>
|
||||
<div class="layout-mock layout-mock--wrap2">
|
||||
<i class="tile tile-a"></i>
|
||||
<i class="tile tile-b"></i>
|
||||
<i class="tile tile-c"></i>
|
||||
<i class="tile tile-d"></i>
|
||||
</div>
|
||||
<div class="layout-pick-title">一行两个</div>
|
||||
<div class="layout-pick-desc">两列卡片,适合配入口图</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mb-2 mt-6 font-medium text-[hsl(var(--foreground))]">
|
||||
待办入口图
|
||||
</div>
|
||||
<div class="mb-3 text-sm text-[hsl(var(--muted-foreground))]">
|
||||
图标入口展示时使用。未上传则用患者端默认图(也可把生成图上传到这里替换)。
|
||||
</div>
|
||||
<div class="remind-icon-grid">
|
||||
<div
|
||||
v-for="item in REMIND_ICON_FIELDS"
|
||||
:key="item.key"
|
||||
class="remind-icon-cell"
|
||||
>
|
||||
<FormAvatar
|
||||
:value="remindIcons[item.key]"
|
||||
@update:value="(url) => (remindIcons[item.key] = url || '')"
|
||||
/>
|
||||
<div class="remind-icon-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2 mt-6 font-medium text-[hsl(var(--foreground))]">
|
||||
订单待办样式
|
||||
</div>
|
||||
<Radio.Group v-model:value="zoneLayout" class="mb-6">
|
||||
<Radio value="scroll">横向滑动</Radio>
|
||||
<Radio value="wrap">换行(一行五个)</Radio>
|
||||
<Radio value="wrap2">一行两个</Radio>
|
||||
</Radio.Group>
|
||||
<div class="mb-2 font-medium text-[hsl(var(--foreground))]">订单提醒</div>
|
||||
<div class="mb-2 text-sm text-[hsl(var(--muted-foreground))]">
|
||||
首页展示待支付挂号、待支付商品、已发货(快递待收货)、已到货待确认收货(自提)。
|
||||
</div>
|
||||
<Radio.Group v-model:value="orderRemindStyle">
|
||||
<Radio value="grid">一行两列卡片</Radio>
|
||||
<Radio value="zone">金刚区图标</Radio>
|
||||
<Radio value="zone">图标入口</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
<div class="cfg-panel-footer">
|
||||
@@ -98,3 +212,118 @@ onMounted(load);
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.layout-pick {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.layout-pick-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
background: hsl(var(--card, var(--background)));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
.layout-pick-card.is-active {
|
||||
border: 1px solid hsl(var(--primary) / 30%);
|
||||
box-shadow: 0 0 6px hsl(var(--primary) / 18%);
|
||||
}
|
||||
.layout-pick-title {
|
||||
margin-top: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
.layout-pick-desc {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
.layout-mock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 56px;
|
||||
padding: 8px;
|
||||
background: hsl(var(--muted) / 0.25);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 8px;
|
||||
}
|
||||
.layout-mock--scroll {
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layout-mock--wrap {
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.layout-mock--wrap2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
.dot,
|
||||
.tile {
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.dot {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tile {
|
||||
height: 18px;
|
||||
}
|
||||
.dot-a,
|
||||
.tile-a {
|
||||
background: #4175ee;
|
||||
}
|
||||
.dot-b,
|
||||
.tile-b {
|
||||
background: #00b578;
|
||||
}
|
||||
.dot-c,
|
||||
.tile-c {
|
||||
background: #e67e00;
|
||||
}
|
||||
.dot-d,
|
||||
.tile-d {
|
||||
background: #7c3aed;
|
||||
}
|
||||
.dot-fade {
|
||||
opacity: 0.35;
|
||||
background: hsl(var(--muted-foreground));
|
||||
}
|
||||
.dot-empty {
|
||||
background: transparent;
|
||||
border: 1px dashed hsl(var(--border));
|
||||
}
|
||||
.remind-icon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
.remind-icon-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
.remind-icon-label {
|
||||
font-size: 13px;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.layout-pick,
|
||||
.remind-icon-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user