已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
@@ -103,6 +103,19 @@ const pageRules: Record<string, ResourcePageRule> = {
|
||||
'produced_at',
|
||||
],
|
||||
},
|
||||
dev_usage_stat: {
|
||||
editKeys: [
|
||||
'stat_date',
|
||||
'usage_kg',
|
||||
'breakfast_kg',
|
||||
'lunch_kg',
|
||||
'dinner_kg',
|
||||
'source',
|
||||
'calc_version',
|
||||
'calculated_at',
|
||||
],
|
||||
editSubmitOnlyKeys: ['product_info_identity'],
|
||||
},
|
||||
product_repair: {
|
||||
editKeys: [
|
||||
'repair_no',
|
||||
@@ -144,6 +157,9 @@ const pageRules: Record<string, ResourcePageRule> = {
|
||||
ec_product_image: {
|
||||
editKeys: ['ec_product_identity', 'image_uri', 'sort_no', 'is_cover'],
|
||||
},
|
||||
deposit_policy: {
|
||||
editKeys: ['product_type_identity', 'name', 'amount', 'rule_text'],
|
||||
},
|
||||
fin_settlement: {
|
||||
editKeys: [
|
||||
'settlement_no',
|
||||
|
||||
@@ -149,6 +149,14 @@ export type ResourceUiDefinition = {
|
||||
};
|
||||
|
||||
const fieldLabels: Record<string, string> = {
|
||||
// 用气统计字段须注册到公共字典,资源初始化和详情展示共用此名称。
|
||||
stat_date: '统计日期',
|
||||
usage_kg: '用气量(kg)',
|
||||
breakfast_kg: '早餐用气(kg)',
|
||||
lunch_kg: '午餐用气(kg)',
|
||||
dinner_kg: '晚餐用气(kg)',
|
||||
calc_version: '计算口径版本',
|
||||
calculated_at: '计算时间',
|
||||
code: '编码',
|
||||
producer_code: '生产商编码',
|
||||
name: '名称',
|
||||
@@ -190,6 +198,7 @@ const fieldLabels: Record<string, string> = {
|
||||
started_at: '开始时间',
|
||||
completed_at: '完成时间',
|
||||
appointment_at: '预约时间',
|
||||
deposit_amount: '气瓶押金(分)',
|
||||
result: '检修结果',
|
||||
target_product_status: '目标产品状态',
|
||||
content: '内容',
|
||||
@@ -218,6 +227,22 @@ const fieldLabels: Record<string, string> = {
|
||||
pay_channel: '支付渠道',
|
||||
pay_type: '支付类型',
|
||||
amount: '金额(元)',
|
||||
rule_text: '退押规则',
|
||||
deposit_no: '押金编号',
|
||||
deposit_status: '押金状态',
|
||||
refunded_at: '退回时间',
|
||||
policy_identity: '押金规则',
|
||||
gasorder_identity: '来源供气订单',
|
||||
return_status: '退瓶状态',
|
||||
address_snapshot: '上门地址',
|
||||
appointment_start: '预约开始时间',
|
||||
appointment_end: '预约结束时间',
|
||||
estimated_amount: '预计退款金额(分)',
|
||||
deduction_amount: '验收扣减金额(分)',
|
||||
refund_amount: '实际退款金额(分)',
|
||||
inspection_remark: '验收说明',
|
||||
picked_up_at: '实际回收时间',
|
||||
inspected_at: '完成验收时间',
|
||||
fee: '手续费(元)',
|
||||
args: '支付参数',
|
||||
callback_msg: '回调信息',
|
||||
@@ -270,6 +295,8 @@ const fieldLabels: Record<string, string> = {
|
||||
sort_no: '排序',
|
||||
product_code: '商品编码',
|
||||
product_type_name: '产品类型名称',
|
||||
device_kind: '设备分类',
|
||||
vendor_device_id: '厂商设备编号',
|
||||
product_params: '产品规格参数',
|
||||
price_amount: '售价(元)',
|
||||
stock_quantity: '库存',
|
||||
@@ -549,7 +576,14 @@ export const resources: ResourceUiDefinition[] = [
|
||||
define('producer_account', '生产商管理', 'writable', [f('producer_code', { required: true }), f('name', { required: true }), f('credit_code'), f('principal'), f('phone'), f('address', { emptyText: '未填写', placeholder: '请输入地址' }), f('username', { required: true }), f('password', { required: true }), f('display_name'), producerAdminRole(), f('remark')]),
|
||||
define('product_type', '智能气阀类型', 'editable', [f('code', { required: true }), f('name', { required: true })]),
|
||||
define('product_warehouse', '智能气阀库房', 'editable', [f('code', { required: true }), f('name', { required: true }), f('address', { emptyText: '未填写', placeholder: '请输入地址' }), f('manager'), f('phone')]),
|
||||
define('product_info', '智能气阀', 'editable', [f('code', { required: true }), f('name', { required: true }), f('product_status', { type: 'select', options: productStatusOptions, unknownValueLabel: '未知状态' }), relation('producer_account_identity', '/producer_account', true, { label: '生产商', listLabel: '生产商', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true, emptyText: '未填写', placeholder: '请选择生产商' }), relation('product_type_identity', '/product_type', true, { label: '智能气阀类型', listLabel: '智能气阀类型', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true, emptyText: '未填写', placeholder: '请选择智能气阀类型' }), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse', false, { label: '库房', emptyText: '未填写', placeholder: '请选择库房' }), relation('gas_basic_identity', '/gas_basic', false, { label: '气站', emptyText: '未填写', placeholder: '请选择气站' }), relation('delivery_basic_identity', '/delivery_basic', false, { label: '配送点', emptyText: '未填写', placeholder: '请选择配送点' }), relation('user_account_identity', '/user_account', false, { label: '用户', emptyText: '未填写', placeholder: '请选择用户' }), f('produced_at', { required: true })], 'list', [
|
||||
define('product_info', '智能气阀', 'editable', [
|
||||
// 明确的后台分类和厂商编号用于后续用户设备查询,不推断在线或控制能力。
|
||||
f('device_kind', { label: '设备分类', type: 'select', options: [
|
||||
{ label: '未分类', value: 'unknown' }, { label: '智能阀', value: 'valve' },
|
||||
{ label: '报警器', value: 'alarm' }, { label: '钢瓶', value: 'cylinder' },
|
||||
], emptyText: '未分类', placeholder: '请选择设备分类' }),
|
||||
f('vendor_device_id', { label: '厂商设备编号', emptyText: '未关联', placeholder: '16位数字;非物联网产品留空' }),
|
||||
f('code', { required: true }), f('name', { required: true }), f('product_status', { type: 'select', options: productStatusOptions, unknownValueLabel: '未知状态' }), relation('producer_account_identity', '/producer_account', true, { label: '生产商', listLabel: '生产商', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true, emptyText: '未填写', placeholder: '请选择生产商' }), relation('product_type_identity', '/product_type', true, { label: '智能气阀类型', listLabel: '智能气阀类型', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true, emptyText: '未填写', placeholder: '请选择智能气阀类型' }), f('params', { required: true }), relation('warehouse_identity', '/product_warehouse', false, { label: '库房', emptyText: '未填写', placeholder: '请选择库房' }), relation('gas_basic_identity', '/gas_basic', false, { label: '气站', emptyText: '未填写', placeholder: '请选择气站' }), relation('delivery_basic_identity', '/delivery_basic', false, { label: '配送点', emptyText: '未填写', placeholder: '请选择配送点' }), relation('user_account_identity', '/user_account', false, { label: '用户', emptyText: '未填写', placeholder: '请选择用户' }), f('produced_at', { required: true })], 'list', [
|
||||
{ name: '修改智能气阀状态', resource: '/product_info/:identity/lifecycle', method: 'PATCH', fields: [f('product_status', { label: '目标状态', required: true, type: 'select', options: productStatusOptions, placeholder: '请选择目标状态' })] },
|
||||
{ name: '变更智能气阀归属', resource: '/product_info/:identity', method: 'PUT', fields: [relation('warehouse_identity', '/product_warehouse'), relation('gas_basic_identity', '/gas_basic'), relation('delivery_basic_identity', '/delivery_basic'), relation('user_account_identity', '/user_account'), f('action', { required: true, type: 'select', options: [{ label: '入库', value: 'warehouse' }, { label: '分配', value: 'assigned' }, { label: '归还', value: 'returned' }, { label: '人工调整', value: 'manual' }] }), f('reason', { required: true }), f('remark')] },
|
||||
]),
|
||||
@@ -699,7 +733,11 @@ export const resources: ResourceUiDefinition[] = [
|
||||
define('ec_category', '商品分类', 'writable', [relation('parent_identity', '/ec_category'), f('name', { required: true }), f('sort_no')], 'tree'),
|
||||
define('ec_product', '商品', 'writable', [relation('ec_category_identity', '/ec_category', true), f('product_code', { required: true }), f('name', { required: true }), f('price_amount', { required: true }), f('stock_quantity')]),
|
||||
define('ec_product_attribute', '商品属性', 'writable', [relation('ec_product_identity', '/ec_product', true), f('name', { required: true }), f('value', { required: true }), f('sort_no')]),
|
||||
define('ec_product_image', '商品图片', 'writable', [relation('ec_product_identity', '/ec_product', true), f('image_uri', { required: true }), f('sort_no'), f('is_cover')]),
|
||||
// 商品图片关联显示商品名称,列表沿用关系详情导航,内部标识仍用于保存。
|
||||
define('ec_product_image', '商品图片', 'writable', [relation('ec_product_identity', '/ec_product', true, {
|
||||
label: '商品', listLabel: '商品', listRelationNameOnly: true,
|
||||
displayRelationLabel: true, showIdentityCopy: true,
|
||||
}), f('image_uri', { required: true, label: '商品图片' }), f('sort_no'), f('is_cover')]),
|
||||
define('ec_cart', '购物车', 'readonly', [
|
||||
relation('user_account_identity', '/user_account', false, {
|
||||
label: '用户', listLabel: '用户', listRelationNameOnly: true,
|
||||
@@ -747,6 +785,54 @@ export const resources: ResourceUiDefinition[] = [
|
||||
displayRelationLabel: true, showIdentityCopy: true,
|
||||
}), f('score'), f('content'),
|
||||
]),
|
||||
define('dev_usage_stat', '用气统计数据', 'editable', [
|
||||
relation('product_info_identity', '/product_info', true, { label: '智能设备', listLabel: '智能设备', listRelationNameOnly: true, displayRelationLabel: true, showIdentityCopy: true }),
|
||||
f('stat_date', { label: '统计日期', required: true }),
|
||||
f('usage_kg', { label: '用气量(kg)', required: true }),
|
||||
f('breakfast_kg', { label: '早餐用气(kg)', required: true }),
|
||||
f('lunch_kg', { label: '午餐用气(kg)', required: true }),
|
||||
f('dinner_kg', { label: '晚餐用气(kg)', required: true }),
|
||||
f('source', { label: '数据来源', required: true, type: 'select', options: [
|
||||
{ label: '计量表', value: 'meter' }, { label: '设备上报', value: 'device' }, { label: '人工导入', value: 'manual' },
|
||||
] }),
|
||||
f('calc_version', { label: '计算口径版本', required: true }),
|
||||
f('calculated_at', { label: '计算时间', required: true }),
|
||||
]),
|
||||
define('deposit_policy', '押金规则', 'writable', [
|
||||
relation('product_type_identity', '/product_type', true, { label: '气瓶类型', listLabel: '气瓶类型' }),
|
||||
f('name', { required: true, label: '规则名称' }),
|
||||
f('amount', { required: true, label: '单瓶押金(分)' }),
|
||||
f('rule_text', { required: true, label: '退押规则' }),
|
||||
]),
|
||||
define('deposit_record', '押金记录', 'readonly', [
|
||||
relation('user_account_identity', '/user_account', false, { label: '用户' }),
|
||||
relation('product_info_identity', '/product_info', false, { label: '绑定气瓶' }),
|
||||
relation('policy_identity', '/deposit_policy', false, { label: '押金规则' }),
|
||||
relation('gasorder_identity', '/gasorder_basic', false, { label: '来源供气订单', emptyText: '历史导入' }),
|
||||
f('deposit_no', { label: '押金编号' }),
|
||||
f('amount', { label: '押金金额(分)' }),
|
||||
f('deposit_status', { type: 'select', label: '押金状态', options: [
|
||||
{ label: '使用中', value: 10 }, { label: '退款中', value: 20 }, { label: '已退回', value: 23 },
|
||||
] }),
|
||||
f('paid_at', { label: '缴纳时间' }), f('refunded_at', { label: '退回时间', emptyText: '尚未退回' }),
|
||||
]),
|
||||
define('deposit_return_request', '退瓶处理', 'readonly', [
|
||||
f('request_no', { label: '申请编号' }),
|
||||
f('return_status', { type: 'select', options: [
|
||||
{ label: '待上门', value: 10 }, { label: '已回收待验收', value: 20 },
|
||||
{ label: '已验收待退款', value: 30 }, { label: '已完成', value: 40 }, { label: '已取消', value: 50 },
|
||||
] }),
|
||||
f('address_snapshot'), f('contact_name'), f('contact_phone'),
|
||||
f('appointment_start'), f('appointment_end'), f('estimated_amount'),
|
||||
f('deduction_amount'), f('refund_amount'), f('inspection_remark'),
|
||||
f('operator_identity'), f('picked_up_at'), f('inspected_at'), f('completed_at'),
|
||||
], 'list', [
|
||||
{ name: '确认已上门回收', resource: '/deposit_return_request/:identity/confirm-pickup', visibleFor: { field: 'return_status', values: [10] } },
|
||||
{ name: '提交验收结果', resource: '/deposit_return_request/:identity/inspect', fields: [
|
||||
f('deduction_amount', { required: true }), f('remark', { required: true, label: '验收说明' }),
|
||||
], visibleFor: { field: 'return_status', values: [20] } },
|
||||
{ name: '退入用户余额', resource: '/deposit_return_request/:identity/complete-refund', visibleFor: { field: 'return_status', values: [30] } },
|
||||
]),
|
||||
|
||||
define('wallet_basic', '钱包', 'readonly', [f('owner_type'), f('owner_identity', {
|
||||
type: 'identity', label: '归属主体', listRelationNameOnly: true,
|
||||
@@ -892,6 +978,8 @@ export const resources: ResourceUiDefinition[] = [
|
||||
options: [
|
||||
{ label: '公告', value: 'notice' },
|
||||
{ label: '协议', value: 'agreement' },
|
||||
{ label: '安全宣传', value: 'safety_article' },
|
||||
{ label: '法律法规', value: 'law' },
|
||||
],
|
||||
unknownValueLabel: '未知内容类型',
|
||||
}),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -223,6 +223,14 @@ const routes: AppRouteRecordRaw[] = [
|
||||
'/product_info',
|
||||
'product_info',
|
||||
),
|
||||
child(
|
||||
'product',
|
||||
'usage-statistics',
|
||||
'usage-statistics',
|
||||
'用气统计数据',
|
||||
'/dev_usage_stat',
|
||||
'dev_usage_stat',
|
||||
),
|
||||
child(
|
||||
'product',
|
||||
'repair',
|
||||
@@ -386,8 +394,6 @@ const routes: AppRouteRecordRaw[] = [
|
||||
'商品图片',
|
||||
'/ec_product_image',
|
||||
'ec_product',
|
||||
true,
|
||||
'ec-products',
|
||||
),
|
||||
child('ec', 'carts', 'carts', '购物车', '/ec_cart', 'ec_cart'),
|
||||
child('ec', 'orders', 'orders', '商城订单', '/ec_order', 'ec_order'),
|
||||
@@ -402,6 +408,9 @@ const routes: AppRouteRecordRaw[] = [
|
||||
'ec-orders',
|
||||
),
|
||||
child('ec', 'reviews', 'reviews', '商品评价', '/ec_review', 'ec_review'),
|
||||
child('ec', 'deposit-policies', 'deposit-policies', '押金规则', '/deposit_policy', 'deposit_policy'),
|
||||
child('ec', 'deposit-records', 'deposit-records', '押金记录', '/deposit_record', 'deposit_record'),
|
||||
child('ec', 'deposit-returns', 'deposit-returns', '退瓶处理', '/deposit_return_request', 'deposit_return_request'),
|
||||
]),
|
||||
financeRoute,
|
||||
group('content', 'content', '内容管理', 'icon-file', 100, [
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<!-- 功能:直接上传并预览商品图片,存储地址仅在内部回填;版本:1.1.0。 -->
|
||||
<template>
|
||||
<div class="product-image-field">
|
||||
<img v-if="modelValue" :src="previewURL" alt="商品图片预览" @error="previewFailed = true" />
|
||||
<span v-if="previewFailed">图片预览失败,请重新上传</span>
|
||||
<input ref="picker" type="file" accept="image/jpeg,image/png" hidden @change="selectFile" />
|
||||
<a-button :loading="busy" :disabled="disabled" @click="picker?.click()">
|
||||
{{ modelValue ? '重新上传图片' : '上传商品图片' }}
|
||||
</a-button>
|
||||
<small>支持 JPG、PNG,最大 2MB。</small>
|
||||
<span v-if="error" role="alert">{{ error }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { getToken } from '@/utils/auth';
|
||||
const props = defineProps<{ modelValue?: string; disabled?: boolean }>();
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: string] }>();
|
||||
const picker = ref<HTMLInputElement>();
|
||||
const busy = ref(false);
|
||||
const error = ref('');
|
||||
const previewFailed = ref(false);
|
||||
let active = true;
|
||||
let uploadController: AbortController | undefined;
|
||||
// 关闭编辑窗口后取消请求,迟到响应不能回填已离开的商品表单。
|
||||
onBeforeUnmount(() => { active = false; uploadController?.abort(); });
|
||||
const origin = new URL(import.meta.env.VITE_API_BASE_URL || 'http://localhost:12426/heqi/platform/v1', window.location.origin).origin;
|
||||
const previewURL = computed(() => {
|
||||
try { const url = new URL(props.modelValue || '', origin); return ['http:', 'https:'].includes(url.protocol) ? url.href : ''; }
|
||||
catch { return ''; }
|
||||
});
|
||||
watch(() => props.modelValue, () => { previewFailed.value = false; });
|
||||
|
||||
/** 上传成功后只回填URI,商品绑定沿用资源保存接口;失败保留旧图片。 */
|
||||
async function selectFile(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
input.value = '';
|
||||
if (!file || busy.value || props.disabled) return;
|
||||
error.value = '';
|
||||
if (!['image/jpeg', 'image/png'].includes(file.type) || file.size <= 0 || file.size > 2 * 1024 * 1024) {
|
||||
error.value = '请选择不超过2MB的JPG或PNG图片'; return;
|
||||
}
|
||||
const token = getToken();
|
||||
busy.value = true;
|
||||
uploadController = new AbortController();
|
||||
try {
|
||||
const body = new FormData(); body.append('file', file);
|
||||
const response = await fetch(`${origin}/upload/product-image`, { method: 'POST', headers: { Authorization: token || '' }, body, signal: uploadController.signal });
|
||||
const payload = await response.json();
|
||||
if (!response.ok || payload.code !== 0 || typeof payload.details?.uri !== 'string' ||
|
||||
!payload.details.uri.startsWith('/uploads/product-images/')) throw new Error(payload.message || '商品图片上传失败');
|
||||
if (getToken() !== token) throw new Error('登录状态已变化,请重新打开页面');
|
||||
if (!active) return;
|
||||
emit('update:modelValue', payload.details.uri);
|
||||
} catch (reason) { if (active) error.value = reason instanceof Error ? reason.message : '图片上传失败,请重试'; }
|
||||
finally { if (active) busy.value = false; uploadController = undefined; }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-image-field { display: flex; flex-direction: column; gap: 8px; width: 100%; }
|
||||
.product-image-field img { width: 160px; height: 160px; object-fit: contain; border: 1px solid var(--color-border-2); border-radius: 6px; }
|
||||
.product-image-field small { color: var(--color-text-3); }
|
||||
[role='alert'] { color: rgb(var(--danger-6)); }
|
||||
</style>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!-- 功能:展示公开商品图片及大图预览,不向用户展示存储地址;版本:1.0.0。 -->
|
||||
<template>
|
||||
<a-image v-if="source" :src="source" :width="size" :height="size" fit="contain" alt="商品图片">
|
||||
<template #error>图片加载失败</template>
|
||||
</a-image>
|
||||
<span v-else>暂无图片</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
const props = withDefaults(defineProps<{ uri?: string; size?: number }>(), { size: 80 });
|
||||
// 相对素材路径使用 API 源站,兼容旧记录;禁止非 HTTP 图片协议。
|
||||
const source = computed(() => {
|
||||
if (!props.uri) return '';
|
||||
try {
|
||||
const origin = new URL(import.meta.env.VITE_API_BASE_URL || 'http://localhost:12426/heqi/platform/v1', window.location.origin).origin;
|
||||
const url = new URL(props.uri, origin);
|
||||
return ['http:', 'https:'].includes(url.protocol) ? url.href : '';
|
||||
} catch { return ''; }
|
||||
});
|
||||
</script>
|
||||
@@ -8,7 +8,8 @@
|
||||
<div class="detail-grid">
|
||||
<div v-for="entry in entries" :key="entry.key" class="detail-item" :class="{ 'detail-item-wide': entry.wide }">
|
||||
<span class="detail-label">{{ entry.label }}</span>
|
||||
<a-popover v-if="entry.jsonValue" position="left">
|
||||
<ProductImagePreview v-if="definition.name === 'ec_product_image' && entry.key === 'image_uri'" :uri="String(entry.value)" :size="160" />
|
||||
<a-popover v-else-if="entry.jsonValue" position="left">
|
||||
<a-button type="text" size="mini">查看内容</a-button>
|
||||
<template #content><pre class="json-value">{{ entry.value }}</pre></template>
|
||||
</a-popover>
|
||||
@@ -88,6 +89,7 @@ import {
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import type { ResourceUiDefinition } from '@/api/resources';
|
||||
import IdentityText from '@/components/IdentityText.vue';
|
||||
import ProductImagePreview from './ProductImagePreview.vue';
|
||||
import RelationNameText from '@/views/shared/RelationNameText.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -140,6 +140,11 @@
|
||||
<IdentityText :value="String(model[field.key])" />
|
||||
</div>
|
||||
</div>
|
||||
<ProductImageField
|
||||
v-else-if="field.key === 'image_uri' && fields.some((item) => item.key === 'ec_product_identity')"
|
||||
v-model="model[field.key]"
|
||||
:disabled="disabledSet.has(field.key)"
|
||||
/>
|
||||
<a-input
|
||||
v-else
|
||||
v-model="model[field.key]"
|
||||
@@ -162,6 +167,7 @@ import ContractAttachmentField, {
|
||||
type ContractAttachmentFieldState,
|
||||
} from './ContractAttachmentField.vue';
|
||||
import MenuPermissionTree from './MenuPermissionTree.vue';
|
||||
import ProductImageField from './ProductImageField.vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<a-button @click="resetSearch">重置</a-button>
|
||||
</a-form>
|
||||
<a-space class="list-actions">
|
||||
<!-- 商品图片复用商品权限,通过商品列表提供固定入口,不依赖服务端菜单去重。 -->
|
||||
<a-button v-if="definition.name === 'ec_product'" @click="router.push({ name: 'ec-images' })">
|
||||
<template #icon><icon-image /></template>商品图片
|
||||
</a-button>
|
||||
<a-button v-if="managedOwnerIdentity" @click="returnManagedList">
|
||||
<template #icon><icon-left /></template>返回来源列表
|
||||
</a-button>
|
||||
@@ -45,8 +49,12 @@
|
||||
:tooltip="!isProtectedListAvatarField(definition.name, field.key) && !field.listRelationNameOnly"
|
||||
>
|
||||
<template #cell="{ record }">
|
||||
<ProductImagePreview
|
||||
v-if="definition.name === 'ec_product_image' && field.key === 'image_uri'"
|
||||
:uri="String(record[field.key] ?? '')"
|
||||
/>
|
||||
<ProtectedAvatarThumbnail
|
||||
v-if="isProtectedListAvatarField(definition.name, field.key)"
|
||||
v-else-if="isProtectedListAvatarField(definition.name, field.key)"
|
||||
:resource="definition.resource"
|
||||
:identity="String(record.identity ?? '')"
|
||||
:display-name="protectedListAvatarDisplayName(record)"
|
||||
@@ -184,6 +192,7 @@ import {
|
||||
} from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import IdentityText from '@/components/IdentityText.vue';
|
||||
import ProductImagePreview from '@/views/resource/ProductImagePreview.vue';
|
||||
import {
|
||||
displayRawValue,
|
||||
displayResourceField,
|
||||
|
||||
Reference in New Issue
Block a user