主账支持企微通讯录同步人员,并完善工单展示与后台人员/工单页联调能力。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 员工管理:对接主账 /inquiry/staffRole。
|
||||
* 企微 userid 由运营手工绑定,不向企微换号;停用走 disable 接口。
|
||||
* 企微 userid 可手工维护,也可点「同步企业微信」由主账 upsert(姓名/手机/部门)。
|
||||
* 浏览器禁止直连企微;岗位/线路仍手工改。
|
||||
*/
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
@@ -9,13 +10,17 @@ import {
|
||||
disableStaff,
|
||||
getStaffCounts,
|
||||
getStaffList,
|
||||
getWecomDepartments,
|
||||
saveStaff as saveStaffApi,
|
||||
syncWecomStaff,
|
||||
} from "/@/views/inquiry/staffRole/staffRole.api";
|
||||
import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
|
||||
import AdminModal from "/@/views/inquiry/components/AdminModal.vue";
|
||||
|
||||
type StaffRole = "sales" | "air" | "sea" | "land";
|
||||
|
||||
type WecomDeptOption = { id: number; name: string; path: string; parentId: number; depth: number };
|
||||
|
||||
type StaffRow = {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -53,6 +58,10 @@ const filters = reactive({
|
||||
|
||||
const modalOpen = ref(false);
|
||||
const editing = ref<StaffRow | null>(null);
|
||||
const syncing = ref(false);
|
||||
const wecomDepts = ref<WecomDeptOption[]>([]);
|
||||
const wecomDeptsLoading = ref(false);
|
||||
const wecomDeptsLoaded = ref(false);
|
||||
const form = reactive({
|
||||
name: "",
|
||||
wecomId: "",
|
||||
@@ -139,6 +148,82 @@ watch(
|
||||
|
||||
onMounted(() => loadList());
|
||||
|
||||
/**
|
||||
* 打开弹窗时拉企微部门;失败不挡手工保存,仅提示。
|
||||
* 下拉对齐旧项目业务层:只展示「分公司」「旧组织」下的直接子部门
|
||||
*(事业二部/元昇/武汉友通达/通多邦…),不要公司根下的一级中心。
|
||||
*/
|
||||
async function ensureWecomDepartments() {
|
||||
if (wecomDeptsLoaded.value || wecomDeptsLoading.value) {
|
||||
ensureCurrentDeptOption();
|
||||
return;
|
||||
}
|
||||
wecomDeptsLoading.value = true;
|
||||
try {
|
||||
const res: any = await getWecomDepartments();
|
||||
const list = Array.isArray(res) ? res : res?.result || [];
|
||||
const all: WecomDeptOption[] = (list as any[])
|
||||
.map((d) => {
|
||||
const path = String(d.path || d.name || "");
|
||||
const name = String(d.name || path);
|
||||
const parentId = Number(d.parentId ?? d.parentid ?? 0);
|
||||
const depth = path ? path.split(" / ").length - 1 : 0;
|
||||
return {
|
||||
id: Number(d.id),
|
||||
name,
|
||||
path,
|
||||
parentId,
|
||||
depth: depth < 0 ? 0 : depth,
|
||||
};
|
||||
})
|
||||
.filter((d) => d.name && d.id);
|
||||
|
||||
// 旧项目下拉对应层:分公司 + 旧组织 的子节点
|
||||
const businessParentNames = new Set(["分公司", "旧组织"]);
|
||||
const businessParentIds = new Set(
|
||||
all.filter((d) => businessParentNames.has(d.name)).map((d) => d.id),
|
||||
);
|
||||
let picked = all.filter((d) => businessParentIds.has(d.parentId));
|
||||
|
||||
// 兜底:若企微改名导致找不到父节点,仍回退到根下一级,避免空下拉
|
||||
if (!picked.length) {
|
||||
const rootIds = new Set(
|
||||
all.filter((d) => d.parentId === 0 || d.depth === 0).map((d) => d.id),
|
||||
);
|
||||
if (!rootIds.size) {
|
||||
const minDepth = Math.min(...all.map((d) => d.depth));
|
||||
all.filter((d) => d.depth === minDepth).forEach((d) => rootIds.add(d.id));
|
||||
}
|
||||
picked = all.filter((d) => rootIds.has(d.parentId));
|
||||
}
|
||||
|
||||
wecomDepts.value = picked
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name, "zh-CN"))
|
||||
.map((d) => ({ ...d, depth: 0 }));
|
||||
wecomDeptsLoaded.value = true;
|
||||
} catch (e: any) {
|
||||
message.warning(e?.message || "企微部门列表加载失败,可稍后重试");
|
||||
} finally {
|
||||
wecomDeptsLoading.value = false;
|
||||
ensureCurrentDeptOption();
|
||||
}
|
||||
}
|
||||
|
||||
function ensureCurrentDeptOption() {
|
||||
const cur = (form.dept || "").trim();
|
||||
if (!cur) return;
|
||||
const hit = wecomDepts.value.some((d) => d.name === cur || d.path === cur);
|
||||
if (!hit) {
|
||||
wecomDepts.value = [{ id: -1, name: cur, path: cur, parentId: 0, depth: 0 }, ...wecomDepts.value];
|
||||
} else {
|
||||
const byPath = wecomDepts.value.find((d) => d.path === cur);
|
||||
if (byPath && form.dept !== byPath.name) {
|
||||
form.dept = byPath.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openCreate() {
|
||||
editing.value = null;
|
||||
Object.assign(form, {
|
||||
@@ -152,6 +237,9 @@ function openCreate() {
|
||||
status: "active",
|
||||
});
|
||||
modalOpen.value = true;
|
||||
// 每次打开重新拉一级部门,避免会话里还留着旧的「整棵树」缓存
|
||||
wecomDeptsLoaded.value = false;
|
||||
ensureWecomDepartments();
|
||||
}
|
||||
|
||||
function openEdit(row: StaffRow) {
|
||||
@@ -167,6 +255,8 @@ function openEdit(row: StaffRow) {
|
||||
status: row.status,
|
||||
});
|
||||
modalOpen.value = true;
|
||||
wecomDeptsLoaded.value = false;
|
||||
ensureWecomDepartments();
|
||||
}
|
||||
|
||||
async function saveStaff() {
|
||||
@@ -213,8 +303,19 @@ function removeStaff(row: StaffRow) {
|
||||
});
|
||||
}
|
||||
|
||||
function syncWecom() {
|
||||
message.info("企微通讯录同步尚未开放,请手工维护企微 ID");
|
||||
async function syncWecom() {
|
||||
if (syncing.value) return;
|
||||
syncing.value = true;
|
||||
try {
|
||||
const res: any = await syncWecomStaff();
|
||||
const msg = res?.message || (typeof res === "string" ? res : "同步完成");
|
||||
message.success(msg);
|
||||
await loadList();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || "同步失败");
|
||||
} finally {
|
||||
syncing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function routeTags(routes: string) {
|
||||
@@ -229,11 +330,11 @@ function routeTags(routes: string) {
|
||||
<template>
|
||||
<Teleport defer to="#page-topbar-actions">
|
||||
<div class="staff-topbar-actions">
|
||||
<button type="button" class="btn btn-secondary" @click="syncWecom">
|
||||
<ProtoIcon name="refresh" :size="16" />同步企业微信
|
||||
<button type="button" class="btn btn-secondary" :disabled="syncing" @click="syncWecom">
|
||||
<ProtoIcon name="refresh" :size="16" />{{ syncing ? "同步中…" : "同步企业微信" }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" @click="openCreate">
|
||||
<ProtoIcon name="userPlus" :size="16" />新建员工
|
||||
<ProtoIcon name="userPlus" :size="16" />新增员工
|
||||
</button>
|
||||
</div>
|
||||
</Teleport>
|
||||
@@ -363,29 +464,32 @@ function routeTags(routes: string) {
|
||||
|
||||
<AdminModal
|
||||
v-model:open="modalOpen"
|
||||
:title="editing ? '编辑员工' : '新建员工'"
|
||||
:title="editing ? '编辑员工' : '新增员工'"
|
||||
size="md"
|
||||
ok-text="保存"
|
||||
ok-text="确认"
|
||||
@ok="saveStaff"
|
||||
>
|
||||
<div class="form-row">
|
||||
<label class="lbl">姓名 <span class="req">*</span></label>
|
||||
<input v-model="form.name" class="field" type="text" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="lbl">企微 ID <span class="req">*</span></label>
|
||||
<input v-model="form.wecomId" class="field field-mono" type="text" placeholder="如 zhangmin@yt" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="lbl">手机号</label>
|
||||
<input v-model="form.phone" class="field" type="tel" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="lbl">部门</label>
|
||||
<input v-model="form.dept" class="field" type="text" />
|
||||
<label class="lbl">企业微信 ID <span class="req">*</span></label>
|
||||
<input v-model="form.wecomId" class="field field-mono" type="text" placeholder="如 zhangmin@yt" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="lbl">岗位</label>
|
||||
<label class="lbl">所属部门</label>
|
||||
<select v-model="form.dept" class="field" :disabled="wecomDeptsLoading">
|
||||
<option value="">{{ wecomDeptsLoading ? "加载企微部门…" : "请选择部门" }}</option>
|
||||
<option v-for="d in wecomDepts" :key="d.id + '-' + d.name" :value="d.name">{{ d.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="lbl">岗位 <span class="req">*</span></label>
|
||||
<select v-model="form.role" class="field">
|
||||
<option value="sales">销售</option>
|
||||
<option value="air">航线·空运</option>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 员工岗位 API:对接主账 /inquiry/staffRole。
|
||||
* 与 Jeecg /sys/user 分离——本页管企微岗位绑定。
|
||||
* 与 Jeecg /sys/user 分离——本页管企微岗位绑定;同步走主账 /syncWecom,浏览器不直连企微。
|
||||
*/
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
@@ -11,6 +11,8 @@ enum Api {
|
||||
edit = '/inquiry/staffRole/edit',
|
||||
disable = '/inquiry/staffRole/disable',
|
||||
queryById = '/inquiry/staffRole/queryById',
|
||||
syncWecom = '/inquiry/staffRole/syncWecom',
|
||||
wecomDepartments = '/inquiry/staffRole/wecomDepartments',
|
||||
}
|
||||
|
||||
export const getStaffList = (params) => defHttp.get({ url: Api.list, params });
|
||||
@@ -19,3 +21,7 @@ export const saveStaff = (data, isUpdate: boolean) =>
|
||||
isUpdate ? defHttp.put({ url: Api.edit, data }) : defHttp.post({ url: Api.add, data });
|
||||
export const disableStaff = (id: string) => defHttp.post({ url: Api.disable, params: { id } });
|
||||
export const getStaffById = (id: string) => defHttp.get({ url: Api.queryById, params: { id } });
|
||||
/** 主账拉企微通讯录 upsert;返回 { fetched, created, updated, skipped, message } */
|
||||
export const syncWecomStaff = () => defHttp.post({ url: Api.syncWecom });
|
||||
/** 企微部门下拉(主账转发,禁止浏览器直连企微) */
|
||||
export const getWecomDepartments = () => defHttp.get({ url: Api.wecomDepartments });
|
||||
|
||||
@@ -72,6 +72,37 @@ function dash(v: any) {
|
||||
return s ? s : "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* 主账 inq_ticket_status_log.action 是内部码,禁止当备注展示。
|
||||
* transition 与「已报价」等 toStatus 重复,无用户备注时不再跟一段英文。
|
||||
*/
|
||||
const STATUS_LOG_ACTION_LABEL: Record<string, string> = {
|
||||
transition: "",
|
||||
retry: "系统重试",
|
||||
abnormal_handle: "异常已处理",
|
||||
};
|
||||
|
||||
function isInternalActionCode(v: string) {
|
||||
return Object.prototype.hasOwnProperty.call(STATUS_LOG_ACTION_LABEL, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间线第二段:优先用户备注;否则把内部 action 译成中文;纯内部码且无文案则不展示。
|
||||
*/
|
||||
function timelineNote(x: any) {
|
||||
const remark = String(x?.remark ?? "").trim();
|
||||
if (remark && !isInternalActionCode(remark)) {
|
||||
return remark;
|
||||
}
|
||||
const action = String(x?.action ?? "").trim();
|
||||
const key = isInternalActionCode(remark) ? remark : action;
|
||||
if (isInternalActionCode(key)) {
|
||||
const label = STATUS_LOG_ACTION_LABEL[key];
|
||||
return label ? label : "-";
|
||||
}
|
||||
return dash(action);
|
||||
}
|
||||
|
||||
function fmtTime(v: any) {
|
||||
const s = (v ?? "").toString().trim().replace("T", " ");
|
||||
return s ? s.slice(0, 19) : "-";
|
||||
@@ -83,6 +114,90 @@ function fmtMeasure(v: any, unit: string) {
|
||||
return s ? `${s} ${unit}` : "-";
|
||||
}
|
||||
|
||||
/** 业务线英文码 → 中文,对齐旧主账 businessLineLabel。 */
|
||||
function businessLineLabel(v: any) {
|
||||
const raw = String(v ?? "").trim();
|
||||
const upper = raw.toUpperCase();
|
||||
if (upper === "AIR") return "空运";
|
||||
if (upper === "SEA") return "海运";
|
||||
if (upper === "LAND") return "陆运";
|
||||
if (upper === "MULTI") return "多段联运";
|
||||
return raw;
|
||||
}
|
||||
|
||||
/** FP:AIR|… 管道原文禁止当询价需求展示。 */
|
||||
function looksLikeRawFp(v: any) {
|
||||
const raw = String(v ?? "").trim();
|
||||
return raw.startsWith("FP:") || (raw.includes("|") && raw.includes(":"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 询价需求摘要:对齐旧后台 requirementSummaryLines。
|
||||
* 首行 = 业务线 · 路线;后续 = 货量 / 品名。结构化字段优先,不信 demandDetail 管道串。
|
||||
*/
|
||||
function requirementSummaryLines(t: any): string[] {
|
||||
const businessLine = String(t?.businessLine ?? "").trim().toUpperCase();
|
||||
const lineLabel = String(t?.bizTypeLabel ?? "").trim() || businessLineLabel(t?.businessLine);
|
||||
const origin = String(t?.origin ?? "").trim();
|
||||
const destination = String(t?.destination ?? "").trim();
|
||||
const route =
|
||||
origin || destination ? `${origin || "-"} → ${destination || "-"}` : "";
|
||||
const heading = [lineLabel, route].filter(Boolean).join(" · ");
|
||||
const lines: string[] = [];
|
||||
if (heading) lines.push(heading);
|
||||
|
||||
const isAir = businessLine === "AIR" || lineLabel.includes("空运");
|
||||
const isSea = businessLine === "SEA" || lineLabel.includes("海运");
|
||||
const isLand = businessLine === "LAND" || lineLabel.includes("陆运");
|
||||
const quantities: string[] = [];
|
||||
const pieces = t?.pieces;
|
||||
const weight = t?.weightKg;
|
||||
const volume = t?.volumeCbm;
|
||||
const plain = (v: any) => {
|
||||
if (v == null || v === "") return "";
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? String(n) : String(v).trim();
|
||||
};
|
||||
if (isAir) {
|
||||
if (pieces != null && pieces !== "") quantities.push(`件数 ${plain(pieces)}`);
|
||||
if (weight != null && weight !== "") quantities.push(`毛重 ${plain(weight)}kg`);
|
||||
if (volume != null && volume !== "") quantities.push(`体积 ${plain(volume)}CBM`);
|
||||
} else if (isSea) {
|
||||
if (pieces != null && pieces !== "") quantities.push(`件数 ${plain(pieces)}`);
|
||||
if (weight != null && weight !== "") quantities.push(`重量 ${plain(weight)}kg`);
|
||||
if (volume != null && volume !== "") quantities.push(`体积 ${plain(volume)}CBM`);
|
||||
} else if (isLand) {
|
||||
if (volume != null && volume !== "") quantities.push(`体积 ${plain(volume)}CBM`);
|
||||
if (weight != null && weight !== "") quantities.push(`重量 ${plain(weight)}kg`);
|
||||
if (pieces != null && pieces !== "") quantities.push(`件数 ${plain(pieces)}`);
|
||||
} else {
|
||||
if (pieces != null && pieces !== "") quantities.push(`件数 ${plain(pieces)}`);
|
||||
if (weight != null && weight !== "") quantities.push(`重量 ${plain(weight)}kg`);
|
||||
if (volume != null && volume !== "") quantities.push(`体积 ${plain(volume)}CBM`);
|
||||
}
|
||||
if (quantities.length) lines.push(quantities.join(" · "));
|
||||
|
||||
const cargo: string[] = [];
|
||||
const goods = String(t?.goodsName ?? "").trim();
|
||||
if (goods) cargo.push(`${isLand ? "货物品名" : "品名"} ${goods}`);
|
||||
if (isAir) {
|
||||
const customer = String(t?.customerName ?? "").trim();
|
||||
if (customer) cargo.push(`客户 ${customer}`);
|
||||
}
|
||||
if (cargo.length) lines.push(cargo.join(" · "));
|
||||
|
||||
// 后端已 enrich 时直接用;仅当结构化拼不出时才回退已有标题(仍过滤 FP 原文)
|
||||
if (!lines.length) {
|
||||
const title = String(t?.demandTitle ?? "").trim();
|
||||
const detail = String(t?.demandDetail ?? "").trim();
|
||||
if (title && !looksLikeRawFp(title)) lines.push(title);
|
||||
if (detail && !looksLikeRawFp(detail)) {
|
||||
detail.split(/\r?\n/).map((x) => x.trim()).filter(Boolean).forEach((x) => lines.push(x));
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function mapTicket(t: any, quotes: any[] = [], attachments: any[] = [], timeline: any[] = []): TicketRow {
|
||||
const attachRows: TicketAttach[] = (attachments || [])
|
||||
.map((a) => ({
|
||||
@@ -93,15 +208,15 @@ function mapTicket(t: any, quotes: any[] = [], attachments: any[] = [], timeline
|
||||
.filter((a) => a.name);
|
||||
const origin = dash(t.origin);
|
||||
const destination = dash(t.destination);
|
||||
const route =
|
||||
origin !== "-" || destination !== "-"
|
||||
? `${origin === "-" ? "" : origin} → ${destination === "-" ? "" : destination}`.trim()
|
||||
: "";
|
||||
const reqLines = requirementSummaryLines(t);
|
||||
const salesName = String(t.salesName ?? "").trim();
|
||||
// 发起人必须显示名称;企微 userid 仅作最后兜底(主账 enrich 后应已是中文名)
|
||||
const initiator = salesName || t.salesWecomId || "—";
|
||||
return {
|
||||
id: t.workOrderNo || t.id || "",
|
||||
initiator: t.salesName || t.salesWecomId || "—",
|
||||
demandTitle: t.demandTitle || route || "—",
|
||||
demandDetail: t.demandDetail || "",
|
||||
initiator,
|
||||
demandTitle: reqLines[0] || "—",
|
||||
demandDetail: reqLines.slice(1).join("\n"),
|
||||
quotes: (quotes || []).map((q) => ({
|
||||
source: q.sourceLabel || q.source || "报价",
|
||||
price: q.priceText || "-",
|
||||
@@ -111,7 +226,7 @@ function mapTicket(t: any, quotes: any[] = [], attachments: any[] = [], timeline
|
||||
attachRows,
|
||||
timeline: (timeline || []).map((x) => ({
|
||||
status: dash(x.toStatus || x.status),
|
||||
remark: dash(x.remark || x.action),
|
||||
remark: timelineNote(x),
|
||||
time: fmtTime(x.createTime),
|
||||
})),
|
||||
status: t.status || "询价中",
|
||||
@@ -142,6 +257,14 @@ function routeText(row: TicketRow) {
|
||||
return row.demandTitle || "-";
|
||||
}
|
||||
|
||||
/** 列表询价需求第二行起:与旧后台 summary-cell 多行展示一致。 */
|
||||
function demandDetailLines(row: TicketRow) {
|
||||
return String(row?.demandDetail || "")
|
||||
.split(/\r?\n/)
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -350,17 +473,13 @@ function refresh() {
|
||||
<span style="font-size: 12px; font-weight: 700">{{ row.initiator }}</span>
|
||||
</td>
|
||||
<td class="align-top">
|
||||
<div style="font-weight: 800">{{ row.demandTitle }}</div>
|
||||
<div
|
||||
style="
|
||||
font-size: 10px;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
white-space: pre-line;
|
||||
line-height: 1.5;
|
||||
"
|
||||
>
|
||||
{{ row.demandDetail }}
|
||||
<div class="summary-cell">
|
||||
<strong>{{ row.demandTitle }}</strong>
|
||||
<span
|
||||
v-for="(line, idx) in demandDetailLines(row)"
|
||||
:key="row.id + '-demand-' + idx"
|
||||
>{{ line }}</span
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-top">
|
||||
@@ -491,8 +610,9 @@ function refresh() {
|
||||
<ProtoIcon name="route" :size="14" />询价需求
|
||||
</div>
|
||||
<div class="ticket-detail-demand">
|
||||
<div style="font-weight: 800; color: #0f172a">
|
||||
{{ dash(current.bizType) }}:{{ routeText(current) }}
|
||||
<div class="summary-cell">
|
||||
<strong>{{ current.demandTitle }}</strong>
|
||||
<span v-for="(line, idx) in demandDetailLines(current)" :key="'detail-demand-' + idx">{{ line }}</span>
|
||||
</div>
|
||||
<div class="ticket-detail-grid" style="margin-top: 8px">
|
||||
<div class="ticket-detail-item"><label>业务类型</label><span>{{ dash(current.bizType) }}</span></div>
|
||||
@@ -503,7 +623,6 @@ function refresh() {
|
||||
<div class="ticket-detail-item"><label>品名</label><span>{{ current.goodsName }}</span></div>
|
||||
<div class="ticket-detail-item"><label>客户</label><span>{{ current.customerName }}</span></div>
|
||||
</div>
|
||||
<div v-if="current.demandDetail" class="ticket-detail-note">{{ current.demandDetail }}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -570,3 +689,27 @@ function refresh() {
|
||||
</div>
|
||||
</AdminModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 询价需求列:对齐旧后台 summary-cell(首行加粗,后续灰色小字) */
|
||||
.summary-cell {
|
||||
min-width: 230px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.summary-cell strong,
|
||||
.summary-cell span {
|
||||
display: block;
|
||||
}
|
||||
.summary-cell strong {
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
}
|
||||
.summary-cell span {
|
||||
margin-top: 3px;
|
||||
color: #708090;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user