feat/m3-increments #1
70
frontend/src/api/calc.ts
Normal file
70
frontend/src/api/calc.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import request from './request'
|
||||
import type { PageData } from './types'
|
||||
|
||||
/** 绩效台账行(金额/比例一律 str) */
|
||||
export interface LedgerRow {
|
||||
id: number
|
||||
receipt_id: number | null
|
||||
calc_version: number
|
||||
is_active: number
|
||||
calc_type: number
|
||||
business_id: number
|
||||
business_no: string
|
||||
personnel_id: number
|
||||
personnel_name: string
|
||||
role_code: string
|
||||
customer_type: string
|
||||
product_category: string
|
||||
ship_date: string | null
|
||||
receipt_date: string
|
||||
payment_days: number | null
|
||||
rate_text: string
|
||||
converted_profit: string | null
|
||||
performance_amount: string | null
|
||||
barter_applied: number
|
||||
weight_r: number | null
|
||||
payment_weight: string | null
|
||||
payable_amount: string | null
|
||||
settlement_month: string
|
||||
is_probation: number
|
||||
missing_cost: number
|
||||
snapshot_json: Record<string, unknown> | null
|
||||
calc_reason: string | null
|
||||
}
|
||||
|
||||
export interface LedgerQueryParams {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
settlement_month?: string
|
||||
personnel_id?: number
|
||||
business_id?: number
|
||||
is_active?: number
|
||||
calc_type?: number
|
||||
}
|
||||
|
||||
export function listLedger(params: LedgerQueryParams): Promise<PageData<LedgerRow>> {
|
||||
return request({ url: '/v1/calc/ledger', method: 'get', params })
|
||||
}
|
||||
|
||||
export function getLedger(id: number): Promise<LedgerRow> {
|
||||
return request({ url: `/v1/calc/ledger/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/** 核算回款:返回台账行 + repeated(业务级一次性计提标记) */
|
||||
export function calcReceipt(receiptId: number): Promise<{ ledger: LedgerRow; repeated: boolean }> {
|
||||
return request({ url: `/v1/calc/receipts/${receiptId}/calc`, method: 'post' })
|
||||
}
|
||||
|
||||
export function recalcReceipt(receiptId: number, reason: string): Promise<LedgerRow> {
|
||||
return request({
|
||||
url: `/v1/calc/receipts/${receiptId}/recalc`,
|
||||
method: 'post',
|
||||
data: { reason },
|
||||
})
|
||||
}
|
||||
|
||||
export function batchCalc(receiptIds: number[]): Promise<
|
||||
{ receipt_id: number; ok: boolean; repeated?: boolean; error?: string }[]
|
||||
> {
|
||||
return request({ url: '/v1/calc/batch', method: 'post', data: { receipt_ids: receiptIds } })
|
||||
}
|
||||
105
frontend/src/api/settlements.ts
Normal file
105
frontend/src/api/settlements.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import request from './request'
|
||||
import type { PageData } from './types'
|
||||
|
||||
/** 月结状态:1草稿 2待审批 3已审定(=已锁定)4已驳回 */
|
||||
export const SETTLEMENT_STATUS = {
|
||||
DRAFT: 1,
|
||||
PENDING: 2,
|
||||
APPROVED: 3,
|
||||
REJECTED: 4,
|
||||
} as const
|
||||
|
||||
export function settlementStatusLabel(status: number): string {
|
||||
return (
|
||||
{ 1: '草稿', 2: '待审批', 3: '已审定(锁定)', 4: '已驳回' }[status] ?? String(status)
|
||||
)
|
||||
}
|
||||
|
||||
export function settlementStatusTagType(
|
||||
status: number,
|
||||
): 'primary' | 'success' | 'danger' | 'warning' | 'info' {
|
||||
return (
|
||||
({ 1: 'info', 2: 'primary', 3: 'success', 4: 'danger', 5: 'info' } as const)[status] ?? 'info'
|
||||
)
|
||||
}
|
||||
|
||||
export interface SettlementItem {
|
||||
id: number
|
||||
personnel_id: number
|
||||
personnel_name: string
|
||||
role_code: string
|
||||
calc_type: number
|
||||
performance_sum: string
|
||||
payable_sum: string
|
||||
basic_amount: string | null
|
||||
salary_mode: string | null
|
||||
net_amount: string | null
|
||||
is_probation: number
|
||||
probation_note: string | null
|
||||
}
|
||||
|
||||
export interface Settlement {
|
||||
id: number
|
||||
settlement_month: string
|
||||
status: number
|
||||
total_performance: string | null
|
||||
total_payable: string | null
|
||||
person_count: number
|
||||
approval_order_no: string | null
|
||||
submitter_name: string
|
||||
locked_at: string | null
|
||||
locked_by: string | null
|
||||
create_time: string
|
||||
}
|
||||
|
||||
export interface SettlementDetail extends Settlement {
|
||||
items: SettlementItem[]
|
||||
}
|
||||
|
||||
export interface SettlementQueryParams {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
status?: number
|
||||
}
|
||||
|
||||
export function listSettlements(params: SettlementQueryParams): Promise<PageData<Settlement>> {
|
||||
return request({ url: '/v1/settlements', method: 'get', params })
|
||||
}
|
||||
|
||||
export function getSettlement(id: number): Promise<SettlementDetail> {
|
||||
return request({ url: `/v1/settlements/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
export function generateSettlement(month: string): Promise<SettlementDetail> {
|
||||
return request({ url: '/v1/settlements/generate', method: 'post', data: { month } })
|
||||
}
|
||||
|
||||
export function submitSettlement(id: number): Promise<SettlementDetail> {
|
||||
return request({ url: `/v1/settlements/${id}/submit`, method: 'post' })
|
||||
}
|
||||
|
||||
/** 调整单(已锁定月份的补发/扣减) */
|
||||
export interface AdjustmentRow {
|
||||
id: number
|
||||
order_no: string
|
||||
settlement_month: string
|
||||
personnel_name: string
|
||||
direction: number
|
||||
amount: string
|
||||
reason: string
|
||||
status: number
|
||||
}
|
||||
|
||||
export function createAdjustment(data: {
|
||||
settlement_month: string
|
||||
personnel_id: number
|
||||
direction: number
|
||||
amount: string
|
||||
reason: string
|
||||
}): Promise<Record<string, unknown>> {
|
||||
return request({ url: '/v1/settlements/adjustments', method: 'post', data })
|
||||
}
|
||||
|
||||
export function listAdjustments(): Promise<AdjustmentRow[]> {
|
||||
return request({ url: '/v1/settlements/adjustments', method: 'get' })
|
||||
}
|
||||
@ -137,6 +137,20 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/business/rating/index.vue'),
|
||||
meta: { title: '客户评级计算器', group: '业务数据', order: 16 },
|
||||
},
|
||||
{
|
||||
// 绩效工资组(M3 ④):核算台账读登录即可,月结写限管理员与营销后勤
|
||||
// (api 层 require_roles 兜底,页面层不再叠加 requiresAdmin)
|
||||
path: '/calc/ledger',
|
||||
name: 'calc-ledger',
|
||||
component: () => import('@/views/calc/ledger/index.vue'),
|
||||
meta: { title: '绩效台账', group: '绩效工资', order: 17 },
|
||||
},
|
||||
{
|
||||
path: '/settlements',
|
||||
name: 'settlements',
|
||||
component: () => import('@/views/settlements/index.vue'),
|
||||
meta: { title: '月度结算', group: '绩效工资', order: 18 },
|
||||
},
|
||||
],
|
||||
},
|
||||
// 未匹配路由回首页;若未登录会被守卫拦到登录页
|
||||
|
||||
175
frontend/src/views/calc/ledger/index.vue
Normal file
175
frontend/src/views/calc/ledger/index.vue
Normal file
@ -0,0 +1,175 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import * as calcApi from '@/api/calc'
|
||||
import type { LedgerRow } from '@/api/calc'
|
||||
import AppPagination from '@/components/table/AppPagination.vue'
|
||||
import { usePagedList } from '@/composables/usePagedList'
|
||||
import { handledError } from '@/utils/error'
|
||||
|
||||
/**
|
||||
* 绩效核算台账(M3 ④):核算事实的固化点。
|
||||
*
|
||||
* - 核算=把回款固化为台账行(每笔回款仅一次;同业务重复回款仅登记进度)
|
||||
* - 重算=作废旧版本、按当前参数落新版本(管理员/财务授权,原因留痕)
|
||||
* - 行展开看计算快照(所用参数版本与行级折算明细)
|
||||
*/
|
||||
|
||||
const filters = reactive({
|
||||
settlement_month: '',
|
||||
is_active: 1 as number | undefined,
|
||||
})
|
||||
|
||||
const { records, total, pageNum, pageSize, loading, load, search } = usePagedList<
|
||||
LedgerRow,
|
||||
{ settlement_month?: string; is_active?: number }
|
||||
>(calcApi.listLedger, () => ({
|
||||
settlement_month: filters.settlement_month.trim() || undefined,
|
||||
is_active: filters.is_active,
|
||||
}))
|
||||
|
||||
function resetFilters(): void {
|
||||
filters.settlement_month = ''
|
||||
filters.is_active = 1
|
||||
search()
|
||||
}
|
||||
|
||||
const recalcVisible = ref(false)
|
||||
const recalcTarget = ref<LedgerRow | null>(null)
|
||||
const recalcReason = ref('')
|
||||
|
||||
function openRecalc(row: LedgerRow): void {
|
||||
recalcTarget.value = row
|
||||
recalcReason.value = ''
|
||||
recalcVisible.value = true
|
||||
}
|
||||
|
||||
async function submitRecalc(): Promise<void> {
|
||||
if (!recalcTarget.value) return
|
||||
if (!recalcReason.value.trim()) {
|
||||
ElMessage.warning('重算必须填写原因')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await calcApi.recalcReceipt(recalcTarget.value.id, recalcReason.value.trim())
|
||||
ElMessage.success('重算完成(新版本已生效)')
|
||||
recalcVisible.value = false
|
||||
await load()
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<el-form inline class="filters" @submit.prevent>
|
||||
<el-form-item label="归属月">
|
||||
<el-input
|
||||
v-model="filters.settlement_month"
|
||||
placeholder="YYYY-MM"
|
||||
clearable
|
||||
style="width: 140px"
|
||||
@keyup.enter="search"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="版本">
|
||||
<el-select v-model="filters.is_active" style="width: 130px">
|
||||
<el-option label="仅当前生效" :value="1" />
|
||||
<el-option label="含历史版本" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="records" stripe>
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<pre class="snapshot">{{ JSON.stringify(row.snapshot_json ?? {}, null, 2) }}</pre>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="settlement_month" label="归属月" width="90" />
|
||||
<el-table-column prop="business_no" label="业务编号" min-width="150" />
|
||||
<el-table-column prop="personnel_name" label="归属人员" width="100" />
|
||||
<el-table-column prop="role_code" label="岗位" width="110" />
|
||||
<el-table-column prop="receipt_date" label="回款日" width="100" />
|
||||
<el-table-column prop="payment_days" label="天数" width="70" />
|
||||
<el-table-column prop="rate_text" label="计提比例" min-width="90" />
|
||||
<el-table-column prop="converted_profit" label="折算毛利" min-width="100" />
|
||||
<el-table-column prop="performance_amount" label="绩效金额" min-width="100" />
|
||||
<el-table-column prop="payable_amount" label="应付绩效" min-width="100" />
|
||||
<el-table-column label="版本" width="80">
|
||||
<template #default="{ row }">
|
||||
v{{ row.calc_version }}
|
||||
<el-tag v-if="row.is_active === 0" size="small" type="info" disable-transitions>
|
||||
已替代
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.is_active === 1 && row.calc_type === 1"
|
||||
link
|
||||
type="primary"
|
||||
@click="openRecalc(row)"
|
||||
>
|
||||
重算
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AppPagination
|
||||
v-model:page-num="pageNum"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
@change="load"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="recalcVisible" title="重算确认" width="460px">
|
||||
<p class="recalc-warn">
|
||||
重算会作废当前版本(v{{ recalcTarget?.calc_version }})并按当前参数落新版本,
|
||||
历史版本可追溯。请填写重算原因。
|
||||
</p>
|
||||
<el-input
|
||||
v-model="recalcReason"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
maxlength="255"
|
||||
placeholder="重算原因(必填,留痕)"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="recalcVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitRecalc">确认重算</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.filters {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.snapshot {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
background: #f4f5f8;
|
||||
border-radius: 6px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.recalc-warn {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
</style>
|
||||
245
frontend/src/views/settlements/index.vue
Normal file
245
frontend/src/views/settlements/index.vue
Normal file
@ -0,0 +1,245 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import {
|
||||
generateSettlement,
|
||||
getSettlement,
|
||||
listSettlements,
|
||||
settlementStatusLabel,
|
||||
settlementStatusTagType,
|
||||
submitSettlement,
|
||||
type Settlement,
|
||||
type SettlementDetail,
|
||||
} from '@/api/settlements'
|
||||
import AppPagination from '@/components/table/AppPagination.vue'
|
||||
import { usePagedList } from '@/composables/usePagedList'
|
||||
import { handledError } from '@/utils/error'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
|
||||
/**
|
||||
* 月度结算(M3 ④):按确认回款生成快照 → 提交审批 → 通过即锁定。
|
||||
*
|
||||
* 锁定后源数据改动被拦截(回款/发货写路径 400),调整走调整单。
|
||||
* 详情抽屉展示按人明细(三口径净工资与试用期说明)。
|
||||
*/
|
||||
const filters = reactive({
|
||||
status: undefined as number | undefined,
|
||||
})
|
||||
|
||||
const { records, total, pageNum, pageSize, loading, load, search } = usePagedList<
|
||||
Settlement,
|
||||
{ status?: number }
|
||||
>(listSettlements, () => ({ status: filters.status }))
|
||||
|
||||
function resetFilters(): void {
|
||||
filters.status = undefined
|
||||
search()
|
||||
}
|
||||
|
||||
const generateMonth = ref('')
|
||||
const generating = ref(false)
|
||||
|
||||
async function handleGenerate(): Promise<void> {
|
||||
if (!/^\d{4}-\d{2}$/.test(generateMonth.value)) {
|
||||
ElMessage.warning('请输入结算月份(YYYY-MM)')
|
||||
return
|
||||
}
|
||||
generating.value = true
|
||||
try {
|
||||
await generateSettlement(generateMonth.value)
|
||||
ElMessage.success('月结已生成(草稿)')
|
||||
await search()
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
} finally {
|
||||
generating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const detailVisible = ref(false)
|
||||
const detailLoading = ref(false)
|
||||
const detail = ref<SettlementDetail | null>(null)
|
||||
|
||||
async function openDetail(row: Settlement): Promise<void> {
|
||||
detailVisible.value = true
|
||||
detailLoading.value = true
|
||||
try {
|
||||
detail.value = await getSettlement(row.id)
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
detailVisible.value = false
|
||||
} finally {
|
||||
detailLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const submitting = ref(false)
|
||||
|
||||
async function handleSubmit(row: Settlement): Promise<void> {
|
||||
try {
|
||||
const out = await submitSettlement(row.id)
|
||||
ElMessage.success(
|
||||
out.status === 3 ? '审批通过,月结已锁定' : '已提交审批(待财务复核)',
|
||||
)
|
||||
detail.value = await getSettlement(row.id)
|
||||
await load()
|
||||
} catch (error) {
|
||||
handledError(error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="toolbar">
|
||||
<div class="gen-row">
|
||||
<el-input
|
||||
v-model="generateMonth"
|
||||
placeholder="结算月份 YYYY-MM"
|
||||
style="width: 160px"
|
||||
maxlength="7"
|
||||
/>
|
||||
<el-button type="primary" :loading="generating" @click="handleGenerate">
|
||||
生成月结快照
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form inline class="filters" @submit.prevent>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="filters.status" placeholder="全部" clearable style="width: 150px">
|
||||
<el-option label="草稿" :value="1" />
|
||||
<el-option label="待审批" :value="2" />
|
||||
<el-option label="已审定(锁定)" :value="3" />
|
||||
<el-option label="已驳回" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="records" stripe>
|
||||
<el-table-column prop="settlement_month" label="结算月份" width="100" />
|
||||
<el-table-column label="状态" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="settlementStatusTagType(row.status)" size="small" disable-transitions>
|
||||
{{ settlementStatusLabel(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="total_performance" label="绩效合计" min-width="110" />
|
||||
<el-table-column prop="total_payable" label="应付合计" min-width="110" />
|
||||
<el-table-column prop="person_count" label="人数" width="70" />
|
||||
<el-table-column prop="approval_order_no" label="审批单号" min-width="150" />
|
||||
<el-table-column label="提交时间" min-width="150">
|
||||
<template #default="{ row }">{{ formatDateTime(row.create_time) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="130" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openDetail(row)">详情</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 1 || row.status === 4"
|
||||
link
|
||||
type="success"
|
||||
:loading="submitting"
|
||||
@click="handleSubmit(row)"
|
||||
>
|
||||
提交审批
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AppPagination
|
||||
v-model:page-num="pageNum"
|
||||
v-model:page-size="pageSize"
|
||||
:total="total"
|
||||
@change="load"
|
||||
/>
|
||||
|
||||
<el-drawer v-model="detailVisible" title="月结详情" size="560px">
|
||||
<div v-loading="detailLoading">
|
||||
<template v-if="detail">
|
||||
<div class="detail-head">
|
||||
<span class="detail-month">{{ detail.settlement_month }}</span>
|
||||
<el-tag :type="settlementStatusTagType(detail.status)" size="small" disable-transitions>
|
||||
{{ settlementStatusLabel(detail.status) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="detail-meta">
|
||||
<span>审批单号:{{ detail.approval_order_no || '—' }}</span>
|
||||
<span>提交人:{{ detail.submitter_name }}</span>
|
||||
<span v-if="detail.locked_by">锁定人:{{ detail.locked_by }}</span>
|
||||
</div>
|
||||
<el-table :data="detail.items" size="small" stripe>
|
||||
<el-table-column prop="personnel_name" label="人员" min-width="90" />
|
||||
<el-table-column label="类型" width="90">
|
||||
<template #default="{ row }">
|
||||
{{ row.calc_type === 1 ? '单笔回款' : '经理聚合' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="performance_sum" label="绩效" min-width="90" />
|
||||
<el-table-column prop="payable_sum" label="应付" min-width="90" />
|
||||
<el-table-column prop="net_amount" label="净工资" min-width="90" />
|
||||
<el-table-column label="试用期" width="70">
|
||||
<template #default="{ row }">{{ row.is_probation === 1 ? '是' : '否' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<p v-if="detail.status === 1 || detail.status === 4" class="hint">
|
||||
草稿/已驳回状态可重新生成快照后再次提交。
|
||||
</p>
|
||||
<p v-else-if="detail.status === 3" class="hint">已锁定:源数据改动只产生提示与调整单。</p>
|
||||
</template>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gen-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filters {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.detail-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.detail-month {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: var(--ys-ink);
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 10px 0;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user