Compare commits
22 Commits
d9a20c906b
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 64fdaba93d | |||
| 50d5f54015 | |||
| a77592dec6 | |||
| 00867ff960 | |||
| 4c08bd1f8c | |||
| ab6f5c787e | |||
| 79a7ea8c56 | |||
| b5ba0ca3fb | |||
| 9b5e2807b5 | |||
| 3a30e81183 | |||
| 28386f5429 | |||
| b0b8689acb | |||
| 8012cb82fc | |||
| 73b2da2d1e | |||
| 6f8e5036f7 | |||
| daab8ec41a | |||
| b513d701d8 | |||
| f8eaa554b3 | |||
| 9cee70bc62 | |||
| ffd917815d | |||
| 3841fcc7e5 | |||
| ac7bfeacfe |
+4
-1
@@ -1,2 +1,5 @@
|
|||||||
# 生产环境
|
# 生产环境
|
||||||
VITE_API_BASE_URL = https://api.cpms.example.com/admin
|
VITE_API_BASE_URL = https://cpmsapi.yunvip123.com
|
||||||
|
|
||||||
|
# 是否开启本地调试
|
||||||
|
LOCAL_DEBUG=false
|
||||||
|
|||||||
Generated
+1721
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,15 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vite",
|
"serve": "vite",
|
||||||
|
"serve:prod": "vite --mode production",
|
||||||
"build:dev": "vue-tsc --noEmit && vite build --mode development",
|
"build:dev": "vue-tsc --noEmit && vite build --mode development",
|
||||||
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
||||||
"build:prod": "vue-tsc --noEmit && vite build --mode production",
|
"build:prod": "vue-tsc --noEmit && vite build --mode production",
|
||||||
|
"test": "vitest",
|
||||||
|
"test:run": "vitest run",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
"preview:prod": "pnpm build:prod && vite preview",
|
||||||
"type-check": "vue-tsc --noEmit",
|
"type-check": "vue-tsc --noEmit",
|
||||||
"lint": "eslint src --ext .js,.ts,.tsx",
|
"lint": "eslint src --ext .js,.ts,.tsx",
|
||||||
"lint:fix": "eslint src --ext .js,.ts,.tsx --fix",
|
"lint:fix": "eslint src --ext .js,.ts,.tsx --fix",
|
||||||
@@ -42,17 +47,21 @@
|
|||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"@vitejs/plugin-vue": "^4.5.0",
|
"@vitejs/plugin-vue": "^4.5.0",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||||
|
"@vitest/coverage-v8": "2.1.9",
|
||||||
|
"@vue/test-utils": "^2.4.11",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"eslint-plugin-vue": "^9.0.0",
|
"eslint-plugin-vue": "^9.0.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
|
"jsdom": "^19.0.0",
|
||||||
"less": "^4.6.4",
|
"less": "^4.6.4",
|
||||||
"lint-staged": "^15.0.0",
|
"lint-staged": "^15.0.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"typescript": "^5.3.0",
|
"typescript": "^5.3.0",
|
||||||
"vite": "^5.0.0",
|
"vite": "^5.0.0",
|
||||||
"vite-plugin-remove-console": "^2.2.0",
|
"vite-plugin-remove-console": "^2.2.0",
|
||||||
|
"vitest": "^2.1.9",
|
||||||
"vue-tsc": "^2.0.0"
|
"vue-tsc": "^2.0.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# ============================================================
|
||||||
|
# CPMS 运营平台 - 权限标识字符串列表
|
||||||
|
# 生成规则:
|
||||||
|
# 1. 每个页面都有一个 "页面名-查看" 的默认权限
|
||||||
|
# 2. 表格操作列中的按钮生成对应操作权限
|
||||||
|
# 3. 同一关键字的两个状态按钮(如上架/下架、启用/禁用)合并为一个权限
|
||||||
|
# 4. 筛选栏中有"新增"按钮的页面,生成新增权限
|
||||||
|
# 5. 标识为英文,附带中文说明(含按钮所在位置)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# ─── 赛事管理模块 ─────────────────────────────────────────
|
||||||
|
|
||||||
|
events.list.view # 赛事列表-查看(页面默认查看权限)
|
||||||
|
events.list.toggle_status # 赛事列表-上架/下架(表格操作列,根据赛事状态显示"上架"或"下架"按钮,合并为一个权限)
|
||||||
|
events.regulation.view # 赛事规程-查看(赛事列表表格中的"赛事规程"列,点击"查看"链接)
|
||||||
|
|
||||||
|
events.orders.view # 订单管理-查看(页面默认查看权限)
|
||||||
|
events.orders.refund # 订单管理-重新退款(表格操作列,退款状态为3时显示"重新退款"按钮)
|
||||||
|
|
||||||
|
events.users.view # 用户列表-查看(页面默认查看权限)
|
||||||
|
events.users.toggle_status # 用户列表-启用/禁用(表格操作列,根据用户状态显示"启用"或"禁用"按钮,合并为一个权限)
|
||||||
|
|
||||||
|
events.banner.view # Banner配置-查看(页面默认查看权限)
|
||||||
|
events.banner.add # Banner配置-新增(筛选栏中的"新增"按钮)
|
||||||
|
events.banner.edit # Banner配置-编辑(表格操作列中的"编辑"按钮)
|
||||||
|
events.banner.toggle_status # Banner配置-启用/禁用(表格操作列,根据状态显示"启用"或"禁用"按钮,合并为一个权限)
|
||||||
|
events.banner.delete # Banner配置-删除(表格操作列中的"删除"按钮,红色危险样式)
|
||||||
|
|
||||||
|
events.logs.view # 赛事操作日志-查看(页面默认查看权限,无操作列按钮)
|
||||||
|
|
||||||
|
# ─── 账务管理模块 ─────────────────────────────────────────
|
||||||
|
|
||||||
|
finance.withdraw.view # 提现申请-查看(页面默认查看权限)
|
||||||
|
finance.withdraw.audit # 提现申请-审核(表格操作列,审核状态为"待审核"时显示"审核"按钮)
|
||||||
|
|
||||||
|
finance.wallet.view # 用户钱包-查看(页面默认查看权限)
|
||||||
|
finance.wallet.detail # 用户钱包-查看明细(表格操作列中的"明细"按钮,打开交易明细弹窗)
|
||||||
|
|
||||||
|
finance.payments.view # 支付流水-查看(页面默认查看权限,无操作列按钮,仅展示数据+金额汇总)
|
||||||
|
|
||||||
|
finance.reports.view # 账务报表-查看(页面默认查看权限,无操作列按钮,展示统计卡+趋势图+明细表)
|
||||||
|
|
||||||
|
# ─── 系统管理模块 ─────────────────────────────────────────
|
||||||
|
|
||||||
|
system.users.view # 用户管理-查看(页面默认查看权限)
|
||||||
|
system.users.add # 用户管理-新增(筛选栏中的"新增"按钮)
|
||||||
|
system.users.edit # 用户管理-编辑(表格操作列中的"编辑"按钮)
|
||||||
|
system.users.toggle_status # 用户管理-启用/禁用(表格操作列,根据用户状态显示"启用"或"禁用"按钮,合并为一个权限)
|
||||||
|
|
||||||
|
system.roles.view # 角色权限-查看(页面默认查看权限)
|
||||||
|
system.roles.add # 角色权限-新增(筛选栏中的"新增角色"按钮)
|
||||||
|
system.roles.edit # 角色权限-编辑(表格操作列中的"编辑"按钮)
|
||||||
|
system.roles.delete # 角色权限-删除(表格操作列中的"删除"按钮,红色危险样式)
|
||||||
|
|
||||||
|
system.logs.view # 操作日志-查看(页面默认查看权限,无操作列按钮)
|
||||||
|
|
||||||
|
system.params.view # 参数配置-查看
|
||||||
Generated
+1045
-1
File diff suppressed because it is too large
Load Diff
@@ -101,4 +101,6 @@ export interface WithdrawInfoVO {
|
|||||||
auditImg: string;
|
auditImg: string;
|
||||||
/** 审核图片列表 */
|
/** 审核图片列表 */
|
||||||
auditImgList: string[];
|
auditImgList: string[];
|
||||||
|
/** 审核结果 */
|
||||||
|
checkMsg: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,13 +83,37 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.divisionList {
|
.divisionList {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coverWrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: center;
|
||||||
gap: 6px;
|
align-items: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
:global(.ant-image) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.divisionItem {
|
.divisionItem {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: rgba(0, 0, 0, 0.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1677ff;
|
||||||
|
background: #e6f4ff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,13 @@ const ID_CARD_IMG_COLUMN: any = {
|
|||||||
record ? renderIdImages(record.idCardImgList || [], record.idCardImg || '') : <span>--</span>,
|
record ? renderIdImages(record.idCardImgList || [], record.idCardImg || '') : <span>--</span>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ConditionalColumnKey = 'isRequireIdCardNo' | 'isRequireIdCardImg';
|
||||||
|
|
||||||
|
const CONDITIONAL_PLAYER_COLUMNS: { key: ConditionalColumnKey; column: any }[] = [
|
||||||
|
{ key: 'isRequireIdCardNo', column: ID_CARD_COLUMN },
|
||||||
|
{ key: 'isRequireIdCardImg', column: ID_CARD_IMG_COLUMN },
|
||||||
|
];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'EventDetailModal',
|
name: 'EventDetailModal',
|
||||||
props: {
|
props: {
|
||||||
@@ -94,12 +101,12 @@ export default defineComponent({
|
|||||||
const [detailLoading, setDetailLoading] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
||||||
|
|
||||||
/** 动态列:根据赛事是否要求证件号/证件图片决定显示 */
|
/** 动态列:根据赛事是否要求证件号/证件图片决定显示 */
|
||||||
const playerColumns = computed(() => {
|
const playerColumns = computed(() => [
|
||||||
const cols = [...BASE_PLAYER_COLUMNS];
|
...BASE_PLAYER_COLUMNS,
|
||||||
if (detail.value?.isRequireIdCardNo === '1') cols.push(ID_CARD_COLUMN);
|
...CONDITIONAL_PLAYER_COLUMNS.filter(({ key }) => detail.value?.[key] === '1').map(
|
||||||
if (detail.value?.isRequireIdCardImg === '1') cols.push(ID_CARD_IMG_COLUMN);
|
({ column }) => column,
|
||||||
return cols;
|
),
|
||||||
});
|
]);
|
||||||
|
|
||||||
// ===== 每个组别的选手数据(key = categoryId) =====
|
// ===== 每个组别的选手数据(key = categoryId) =====
|
||||||
const categoryPlayers = ref<Record<string, TournamentAdminInfoSignupVO[]>>({});
|
const categoryPlayers = ref<Record<string, TournamentAdminInfoSignupVO[]>>({});
|
||||||
@@ -206,7 +213,9 @@ export default defineComponent({
|
|||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
|
||||||
<Descriptions.Item label="赛事说明" span={3}>
|
<Descriptions.Item label="赛事说明" span={3}>
|
||||||
{detail.value.descr}
|
<div style={{ whiteSpace: 'pre-wrap', maxHeight: '280px', overflowY: 'auto' }}>
|
||||||
|
{detail.value.descr}
|
||||||
|
</div>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
|
||||||
<Descriptions.Item label="报名起止时间" span={3} class={styles.wideLabel}>
|
<Descriptions.Item label="报名起止时间" span={3} class={styles.wideLabel}>
|
||||||
@@ -261,11 +270,13 @@ export default defineComponent({
|
|||||||
{/* ===== 封面 ===== */}
|
{/* ===== 封面 ===== */}
|
||||||
<div class={styles.section}>
|
<div class={styles.section}>
|
||||||
<div class={styles.sectionTitle}>封面</div>
|
<div class={styles.sectionTitle}>封面</div>
|
||||||
<Image
|
<div class={styles.coverWrapper}>
|
||||||
style={{ maxHeight: '380px' }}
|
<Image
|
||||||
src={detail.value.img || defaultCover}
|
style={{ maxHeight: '380px' }}
|
||||||
fallback={defaultCover}
|
src={detail.value.img || defaultCover}
|
||||||
/>
|
fallback={defaultCover}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ===== 赛事公告 ===== */}
|
{/* ===== 赛事公告 ===== */}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #f0f0f0;
|
border: 1px solid #f0f0f0;
|
||||||
|
max-height: 360px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageGrid {
|
.imageGrid {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default defineComponent({
|
|||||||
const renderAction = (record: any) => {
|
const renderAction = (record: any) => {
|
||||||
const status = record.status;
|
const status = record.status;
|
||||||
|
|
||||||
if (status === '0' || status === '1') {
|
if (['0', '1', '2'].includes(status)) {
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
<Button type="link" size="small" onClick={() => openDetailModal(record)}>
|
<Button type="link" size="small" onClick={() => openDetailModal(record)}>
|
||||||
|
|||||||
@@ -58,11 +58,7 @@ const PLAYER_COLUMNS = [
|
|||||||
key: 'idCardImg',
|
key: 'idCardImg',
|
||||||
width: 170,
|
width: 170,
|
||||||
customRender: ({ record }: { record: TournamentAdminOrderInfoPageVO }) => {
|
customRender: ({ record }: { record: TournamentAdminOrderInfoPageVO }) => {
|
||||||
const urls = record.idCardImgList?.length
|
const urls = record.idCardImgList || [];
|
||||||
? record.idCardImgList
|
|
||||||
: record.idCardImg
|
|
||||||
? [record.idCardImg]
|
|
||||||
: [];
|
|
||||||
if (!urls.length) return <span>--</span>;
|
if (!urls.length) return <span>--</span>;
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'totalBalance',
|
key: 'totalBalance',
|
||||||
label: '平台余额(所有用户余额总和)',
|
label: '平台余额(所有用户余额总和 = 总收入 - 提现金额)',
|
||||||
value: s.totalBalance,
|
value: s.totalBalance,
|
||||||
bgColor: '#e6f4ff',
|
bgColor: '#e6f4ff',
|
||||||
accentColor: '#1677ff',
|
accentColor: '#1677ff',
|
||||||
@@ -224,7 +224,11 @@ export default defineComponent({
|
|||||||
<div class={styles.statValue} style={{ color: item.accentColor }}>
|
<div class={styles.statValue} style={{ color: item.accentColor }}>
|
||||||
¥{formatMoney(item.value)}
|
¥{formatMoney(item.value)}
|
||||||
</div>
|
</div>
|
||||||
{/* 趋势文字暂时隐藏 */}
|
{/* {item.key === 'totalBalance' && (
|
||||||
|
<div class={styles.statDesc} style={{ color: 'rgba(0, 0, 0, 0.45)' }}>
|
||||||
|
= 总收入 - 提现金额
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ export function useReportsModel() {
|
|||||||
// ===== 图表数据 =====
|
// ===== 图表数据 =====
|
||||||
const chartData = computed(() => {
|
const chartData = computed(() => {
|
||||||
const list = data.value?.monthList || [];
|
const list = data.value?.monthList || [];
|
||||||
return list.map((item) => ({
|
// 按月份升序排列,确保图表 X 轴时间顺序正确
|
||||||
|
const sorted = [...list].sort((a, b) => a.month.localeCompare(b.month));
|
||||||
|
return sorted.map((item) => ({
|
||||||
month: dayjs(item.month).format('MM月'),
|
month: dayjs(item.month).format('MM月'),
|
||||||
order: toNumber(item.orderAmount),
|
order: toNumber(item.orderAmount),
|
||||||
refund: toNumber(item.refundAmount),
|
refund: toNumber(item.refundAmount),
|
||||||
|
|||||||
@@ -160,7 +160,8 @@ export default defineComponent({
|
|||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
style={{ width: '140px' }}
|
style={{ width: '140px' }}
|
||||||
value={filterForm.minBalance || undefined}
|
value={filterForm.minBalance || undefined}
|
||||||
precision={0}
|
precision={2}
|
||||||
|
step={0.01}
|
||||||
min={0}
|
min={0}
|
||||||
controls={false}
|
controls={false}
|
||||||
onUpdate:value={(val: any) =>
|
onUpdate:value={(val: any) =>
|
||||||
|
|||||||
@@ -143,9 +143,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== 上传 =====
|
// ===== 上传 =====
|
||||||
.uploader {
|
.uploadWrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploader {
|
||||||
:global {
|
:global {
|
||||||
.ant-upload-list-picture-card-container {
|
.ant-upload-list-picture-card-container {
|
||||||
width: 96px;
|
width: 96px;
|
||||||
@@ -159,6 +164,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pasteTip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #e6f7ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #1677ff;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.uploadTrigger {
|
.uploadTrigger {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { defineComponent, reactive, ref } from 'vue';
|
import { defineComponent, reactive, ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { useEffect } from '@/hooks';
|
import { useEffect } from '@/hooks';
|
||||||
import { Modal, Radio, Input, Button, Upload, Image, Spin, message } from 'ant-design-vue';
|
import { Modal, Radio, Input, Button, Upload, Image, Spin, message } from 'ant-design-vue';
|
||||||
import type { UploadFile } from 'ant-design-vue';
|
import type { UploadFile } from 'ant-design-vue';
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
import { PlusOutlined, CopyOutlined } from '@ant-design/icons-vue';
|
||||||
import { uploadFile, OssUploadType } from '@/utils/oss';
|
import { uploadFile, OssUploadType } from '@/utils/oss';
|
||||||
import { getWithdrawInfo, postWithdrawAudit, type WithdrawInfoVO } from '../model/services';
|
import { getWithdrawInfo, postWithdrawAudit, type WithdrawInfoVO } from '../model/services';
|
||||||
import { getAuditStatusText, getPayStatusText } from '../model/useWithdrawModel';
|
import { getAuditStatusText, getPayStatusText } from '../model/useWithdrawModel';
|
||||||
@@ -41,6 +41,34 @@ export default defineComponent({
|
|||||||
const auditForm = reactive({ pass: true, remark: '' });
|
const auditForm = reactive({ pass: true, remark: '' });
|
||||||
const imageList = ref<UploadFile[]>([]);
|
const imageList = ref<UploadFile[]>([]);
|
||||||
const submitting = ref(false);
|
const submitting = ref(false);
|
||||||
|
const uploadAreaRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
// ===== 粘贴图片上传 =====
|
||||||
|
const handlePaste = async (e: ClipboardEvent) => {
|
||||||
|
if (!props.visible || !uploadAreaRef.value) return;
|
||||||
|
|
||||||
|
const items = e.clipboardData?.items;
|
||||||
|
if (!items) return;
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
if (item.type.indexOf('image') !== -1) {
|
||||||
|
e.preventDefault();
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
await beforeUpload(file as unknown as UploadFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('paste', handlePaste);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('paste', handlePaste);
|
||||||
|
});
|
||||||
|
|
||||||
// ===== 拉取详情 =====
|
// ===== 拉取详情 =====
|
||||||
const fetchDetail = async (id: number) => {
|
const fetchDetail = async (id: number) => {
|
||||||
@@ -239,21 +267,27 @@ export default defineComponent({
|
|||||||
{auditForm.pass && <span class={styles.required}>*</span>}
|
{auditForm.pass && <span class={styles.required}>*</span>}
|
||||||
审核图片
|
审核图片
|
||||||
</span>
|
</span>
|
||||||
<Upload
|
<div ref={uploadAreaRef} class={styles.uploadWrapper}>
|
||||||
listType="picture-card"
|
<Upload
|
||||||
fileList={imageList.value}
|
listType="picture-card"
|
||||||
beforeUpload={beforeUpload}
|
fileList={imageList.value}
|
||||||
onRemove={handleRemove}
|
beforeUpload={beforeUpload}
|
||||||
accept="image/*"
|
onRemove={handleRemove}
|
||||||
class={styles.uploader}
|
accept="image/*"
|
||||||
>
|
class={styles.uploader}
|
||||||
{imageList.value.length >= MAX_IMAGE_COUNT ? null : (
|
>
|
||||||
<div class={styles.uploadTrigger}>
|
{imageList.value.length >= MAX_IMAGE_COUNT ? null : (
|
||||||
<PlusOutlined />
|
<div class={styles.uploadTrigger}>
|
||||||
<div class={styles.uploadText}>请选择图片</div>
|
<PlusOutlined />
|
||||||
</div>
|
<div class={styles.uploadText}>选择或粘贴图片</div>
|
||||||
)}
|
</div>
|
||||||
</Upload>
|
)}
|
||||||
|
</Upload>
|
||||||
|
<div class={styles.pasteTip}>
|
||||||
|
<CopyOutlined />
|
||||||
|
<span>支持 Ctrl+V 粘贴图片</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.footer}>
|
<div class={styles.footer}>
|
||||||
<Button type="primary" onClick={handleSubmit} loading={submitting.value}>
|
<Button type="primary" onClick={handleSubmit} loading={submitting.value}>
|
||||||
@@ -267,6 +301,7 @@ export default defineComponent({
|
|||||||
<div class={styles.infoColumn}>
|
<div class={styles.infoColumn}>
|
||||||
{renderItem('审核时间:', d.auditDate)}
|
{renderItem('审核时间:', d.auditDate)}
|
||||||
{renderItem('审核人:', d.auditNickname)}
|
{renderItem('审核人:', d.auditNickname)}
|
||||||
|
{d.checkMsg && renderItem('审核内容:', d.checkMsg)}
|
||||||
<div class={styles.descItem}>
|
<div class={styles.descItem}>
|
||||||
<span class={styles.descLabel}>审核图片:</span>
|
<span class={styles.descLabel}>审核图片:</span>
|
||||||
{d.auditImgList?.length ? (
|
{d.auditImgList?.length ? (
|
||||||
|
|||||||
@@ -60,15 +60,15 @@ export default defineComponent({
|
|||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
await formRef.value?.validate();
|
await formRef.value?.validate();
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.onSave({
|
props.onSave({
|
||||||
paramCode: formData.paramCode,
|
paramCode: formData.paramCode,
|
||||||
paramValue: formData.paramValue,
|
paramValue: formData.paramValue,
|
||||||
remark: formData.remark,
|
remark: formData.remark,
|
||||||
});
|
});
|
||||||
|
} catch {
|
||||||
|
// 校验未通过,Form 已展示错误,无需处理
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 关闭 */
|
/** 关闭 */
|
||||||
|
|||||||
@@ -1,36 +1,33 @@
|
|||||||
// 参数值单元格:文字最多 2 行省略,末尾带"查看"链接
|
.cell {
|
||||||
:global {
|
display: flex;
|
||||||
.param-value-cell {
|
align-items: center;
|
||||||
display: flex;
|
gap: 6px;
|
||||||
align-items: center;
|
width: 100%;
|
||||||
gap: 6px;
|
cursor: default;
|
||||||
width: 100%;
|
}
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.param-value-text {
|
.text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
color: rgba(0, 0, 0, 0.88);
|
color: rgba(0, 0, 0, 0.88);
|
||||||
}
|
}
|
||||||
|
|
||||||
.param-value-view {
|
.view {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
color: #1677ff;
|
color: #1677ff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { saveParam, updateParam } from './model/services';
|
|||||||
import { useState, useThrottleFn, useContainerSize, useEffect } from '@/hooks';
|
import { useState, useThrottleFn, useContainerSize, useEffect } from '@/hooks';
|
||||||
import ParamFormModal from './components/ParamFormModal';
|
import ParamFormModal from './components/ParamFormModal';
|
||||||
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
import pageStyles from '@/assets/styles/pageLayout.module.less';
|
||||||
import './index.module.less';
|
import cellStyles from './index.module.less';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数值单元格:最多 2 行截断,溢出时显示"查看"按钮
|
* 参数值单元格:最多 2 行截断,溢出时显示"查看"按钮
|
||||||
@@ -38,12 +38,12 @@ const ParamValueCell = defineComponent({
|
|||||||
return () => {
|
return () => {
|
||||||
const raw = props.text;
|
const raw = props.text;
|
||||||
return (
|
return (
|
||||||
<div class="param-value-cell">
|
<div class={cellStyles.cell}>
|
||||||
<div ref={textRef} class="param-value-text">
|
<div ref={textRef} class={cellStyles.text}>
|
||||||
{raw}
|
{raw}
|
||||||
</div>
|
</div>
|
||||||
{overflow.value ? (
|
{overflow.value ? (
|
||||||
<span class="param-value-view" onClick={() => setModalOpen(true)}>
|
<span class={cellStyles.view} onClick={() => setModalOpen(true)}>
|
||||||
查看
|
查看
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
declare module 'china-area-data' {
|
declare module 'china-area-data/data.json' {
|
||||||
const data: Record<string, Record<string, string>>;
|
const data: Record<string, Record<string, string>>;
|
||||||
export default data;
|
export default data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import areaData from 'china-area-data';
|
import areaData from 'china-area-data/data.json';
|
||||||
|
|
||||||
export interface CascadeOption {
|
export interface CascadeOption {
|
||||||
value: string;
|
value: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user