mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-12 03:48:55 +08:00
feat(order): 管理端支持微信小程序发货信息同步、人工补报与审计日志
This commit is contained in:
@@ -78,6 +78,15 @@ export const getOrderList = (params) => {
|
||||
return getRequest(`/order/order`, params)
|
||||
}
|
||||
|
||||
// 小程序专用接口由服务端固定来源与微信支付范围,并回填本地上报结果。
|
||||
export const getWechatOrderList = (params) => getRequest('/order/order/wechat', params)
|
||||
// 状态统计与专用列表共用上报状态筛选。
|
||||
export const getWechatOrderNum = (params) => getRequest('/order/order/wechat/orderNum', params)
|
||||
// 人工补报复用支付流水锁和真实履约校验,不受自动上报开关限制。
|
||||
export const retryWechatOrder = (sn) => postRequest(`/order/order/wechat/${encodeURIComponent(sn)}/retry`, {}, undefined, 120000)
|
||||
// 查询同笔微信支付下的操作日志,不返回原始支付和物流凭据。
|
||||
export const getWechatOrderLogs = (sn, params) => getRequest(`/order/order/wechat/${encodeURIComponent(sn)}/logs`, params)
|
||||
|
||||
// 导出订单列表
|
||||
export const exportOrder = (params) => {
|
||||
return getRequest(`/order/order/queryExportOrder`, params,'blob')
|
||||
|
||||
@@ -182,10 +182,12 @@ export const postBlobRequest = (url, data) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postRequest = (url, params, headers) => {
|
||||
// 个别需等待外部平台的操作可传超时毫秒数,现有调用保持 8 秒默认值。
|
||||
export const postRequest = (url, params, headers, timeout = 8000) => {
|
||||
let accessToken = getStore("accessToken");
|
||||
return service({
|
||||
method: "post",
|
||||
timeout,
|
||||
url: `${url}`,
|
||||
data: params,
|
||||
transformRequest: headers
|
||||
|
||||
23
manager/src/utils/wechatOrderSync.js
Normal file
23
manager/src/utils/wechatOrderSync.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 小程序订单上报状态及日志显示口径,成功仅表示发货接口受理。
|
||||
* @author Bulbasaur
|
||||
* @since 2026-11-09
|
||||
*/
|
||||
export const wechatSyncOptions = [
|
||||
{ value: "UNCHECKED", label: "待核查", type: "info" },
|
||||
{ value: "PENDING", label: "待上报", type: "warning" },
|
||||
{ value: "PROCESSING", label: "处理中", type: "warning" },
|
||||
{ value: "SUCCESS", label: "上报成功", type: "success" },
|
||||
{ value: "FAILED", label: "上报失败", type: "danger" },
|
||||
];
|
||||
|
||||
/** 查找页面和日志的统一状态标签,重复请求跳过使用独立说明。 */
|
||||
export function wechatSyncLabel(value) {
|
||||
if (value === "SKIPPED") return "已跳过重复";
|
||||
return wechatSyncOptions.find((item) => item.value === value)?.label || value || "—";
|
||||
}
|
||||
|
||||
/** 返回 Element Plus 标签类型,未知状态使用中性色。 */
|
||||
export function wechatSyncTag(value) {
|
||||
return wechatSyncOptions.find((item) => item.value === value)?.type || "info";
|
||||
}
|
||||
@@ -52,6 +52,7 @@
|
||||
@change="selectDateRange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<slot name="extra-filters" :form="searchForm" />
|
||||
<el-form-item class="order-search-action">
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
@@ -74,6 +75,9 @@
|
||||
<el-button type="warning" class="export" :loading="exporting" @click="exportOrder">导出订单</el-button>
|
||||
</div>
|
||||
|
||||
<el-alert v-if="loadError" title="订单加载失败,请重试" type="error" :closable="false" show-icon>
|
||||
<el-button link type="primary" @click="init">重新加载</el-button>
|
||||
</el-alert>
|
||||
<el-table v-loading="loading" :data="data" ref="tableRef" class="mt_10" style="width: 100%">
|
||||
<el-table-column prop="sn" label="订单号" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="商品" min-width="280">
|
||||
@@ -126,9 +130,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="下单时间" width="170" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<slot name="extra-columns" />
|
||||
<el-table-column label="操作" :width="actionWidth" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="detail(row)">查看</a>
|
||||
<slot name="extra-actions" :row="row" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -162,6 +168,8 @@ const props = defineProps({
|
||||
fixedClientType: { type: String, default: "" },
|
||||
fixedPaymentMethod: { type: String, default: "" },
|
||||
allowExport: { type: Boolean, default: true },
|
||||
wechatSync: { type: Boolean, default: false },
|
||||
actionWidth: { type: Number, default: 80 },
|
||||
});
|
||||
|
||||
const PROMOTION_OPTIONS = [
|
||||
@@ -204,6 +212,7 @@ function emptySearchForm(pageSize = 20) {
|
||||
orderStatus: "",
|
||||
paymentMethod: props.fixedPaymentMethod,
|
||||
orderPromotionType: "",
|
||||
...(props.wechatSync ? { wechatSyncStatus: "" } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -216,6 +225,9 @@ const searchFormRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const loadError = ref(false);
|
||||
let listRequestSeq = 0;
|
||||
let countRequestSeq = 0;
|
||||
const exporting = ref(false);
|
||||
const pageSizes = ref(PAGE_SIZES);
|
||||
const promotionOptions = ref(PROMOTION_OPTIONS);
|
||||
@@ -389,16 +401,28 @@ function listParams() {
|
||||
return params;
|
||||
}
|
||||
function getDataList() {
|
||||
const requestId = ++listRequestSeq;
|
||||
loading.value = true;
|
||||
API_Order.getOrderList(listParams())
|
||||
loadError.value = false;
|
||||
const fetchList = props.wechatSync ? API_Order.getWechatOrderList : API_Order.getOrderList;
|
||||
fetchList(listParams())
|
||||
.then((res) => {
|
||||
if (requestId !== listRequestSeq) return;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
} else {
|
||||
throw new Error("订单查询失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (requestId !== listRequestSeq) return;
|
||||
data.value = [];
|
||||
total.value = 0;
|
||||
loadError.value = true;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
if (requestId === listRequestSeq) loading.value = false;
|
||||
});
|
||||
}
|
||||
function detail(v) {
|
||||
@@ -454,14 +478,19 @@ function orderStatusClick(name) {
|
||||
getDataList();
|
||||
}
|
||||
function getOrderNumData() {
|
||||
API_Order.getOrderNum(countParams())
|
||||
const requestId = ++countRequestSeq;
|
||||
const fetchCount = props.wechatSync ? API_Order.getWechatOrderNum : API_Order.getOrderNum;
|
||||
fetchCount(countParams())
|
||||
.then((res) => {
|
||||
if (requestId !== countRequestSeq) return;
|
||||
if (res.success) {
|
||||
orderNumData.value = res.result;
|
||||
} else {
|
||||
orderNumData.value = {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
orderNumData.value = {};
|
||||
if (requestId === countRequestSeq) orderNumData.value = {};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -476,6 +505,9 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
// 补报完成后刷新当前筛选和页码,不清空运营人员已选条件。
|
||||
defineExpose({ reload: init });
|
||||
|
||||
onMounted(() => {
|
||||
try {
|
||||
sessionStorage.removeItem("manager-order-list-query");
|
||||
|
||||
88
manager/src/views/order/wechat/SyncLogDrawer.vue
Normal file
88
manager/src/views/order/wechat/SyncLogDrawer.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<!--
|
||||
小程序订单同步日志抽屉:按支付流水展示审计记录,不显示原始身份或支付凭据。
|
||||
@author Bulbasaur
|
||||
@since 2026-11-09
|
||||
-->
|
||||
<template>
|
||||
<el-drawer :model-value="modelValue" title="微信订单同步日志" size="min(1100px, 95vw)" @update:model-value="emit('update:modelValue', $event)">
|
||||
<p>订单:{{ orderSn }}。日志包含同笔微信支付下的关联子订单操作;上报成功仅代表微信接口已受理。</p>
|
||||
<el-button :loading="loading" @click="load">刷新</el-button>
|
||||
<el-alert v-if="failed" title="日志加载失败,请点击刷新重试" type="error" :closable="false" class="mt_10" />
|
||||
<el-table v-loading="loading" :data="records" class="mt_10" empty-text="暂无本地同步记录,历史上报结果需核查">
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<div class="log-detail">
|
||||
<p>触发订单:{{ row.orderSn }}</p>
|
||||
<p>请求摘要:{{ row.summary || '—' }}</p>
|
||||
<p>错误码:{{ row.errorCode || '—' }}</p>
|
||||
<p>处理原因:{{ row.errorMessage || '—' }}</p>
|
||||
<p>结束时间:{{ row.finishTime || '—' }};耗时:{{ row.durationMs == null ? '—' : `${row.durationMs} 毫秒` }}</p>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="操作时间" width="175" />
|
||||
<el-table-column label="操作" width="110">
|
||||
<template #default="{ row }">{{ row.operation === 'RECEIVE' ? '收货通知' : '发货上报' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来源" width="110">
|
||||
<template #default="{ row }">{{ row.triggerType === 'MANUAL' ? '人工补报' : '自动事件' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operatorName" label="操作人" min-width="100" />
|
||||
<el-table-column label="结果" min-width="120">
|
||||
<template #default="{ row }"><el-tag :type="wechatSyncTag(row.status)">{{ wechatSyncLabel(row.status) }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="errorMessage" label="处理原因" min-width="220" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-pagination v-model:current-page="pageNumber" :page-size="20" :total="total" layout="total, prev, pager, next" class="mt_10" @current-change="load" />
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { getWechatOrderLogs } from "@/api/order";
|
||||
import { wechatSyncLabel, wechatSyncTag } from "@/utils/wechatOrderSync";
|
||||
|
||||
const props = defineProps({ modelValue: Boolean, orderSn: { type: String, default: "" } });
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const records = ref([]);
|
||||
const loading = ref(false);
|
||||
const failed = ref(false);
|
||||
const total = ref(0);
|
||||
const pageNumber = ref(1);
|
||||
let requestSeq = 0;
|
||||
|
||||
/** 请求序号隔离不同订单及页码,关闭抽屉后不接受过期响应。 */
|
||||
async function load() {
|
||||
if (!props.modelValue || !props.orderSn) return;
|
||||
const id = ++requestSeq;
|
||||
loading.value = true;
|
||||
failed.value = false;
|
||||
try {
|
||||
const res = await getWechatOrderLogs(props.orderSn, { pageNumber: pageNumber.value, pageSize: 20 });
|
||||
if (id !== requestSeq) return;
|
||||
if (!res.success) throw new Error("日志加载失败");
|
||||
records.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
} catch (e) {
|
||||
if (id !== requestSeq) return;
|
||||
failed.value = true;
|
||||
records.value = [];
|
||||
total.value = 0;
|
||||
} finally {
|
||||
if (id === requestSeq) loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => [props.modelValue, props.orderSn], () => {
|
||||
requestSeq++;
|
||||
records.value = [];
|
||||
total.value = 0;
|
||||
pageNumber.value = 1;
|
||||
loading.value = false;
|
||||
if (props.modelValue) load();
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.log-detail { padding: 0 20px; overflow-wrap: anywhere; }
|
||||
</style>
|
||||
@@ -8,14 +8,76 @@
|
||||
class="wechat-order-notice"
|
||||
description="仅展示微信小程序下单且使用微信支付的订单。自动上报开关开启且微信侧已开通发货管理时,按现有履约流程上报。"
|
||||
/>
|
||||
<OrderList fixed-client-type="WECHAT_MP" fixed-payment-method="WECHAT" :allow-export="false" />
|
||||
<OrderList ref="orderListRef" fixed-client-type="WECHAT_MP" fixed-payment-method="WECHAT" :allow-export="false" wechat-sync :action-width="220">
|
||||
<template #extra-filters="{ form }">
|
||||
<el-form-item label="上报状态" prop="wechatSyncStatus">
|
||||
<el-select v-model="form.wechatSyncStatus" placeholder="全部上报状态" clearable>
|
||||
<el-option v-for="item in wechatSyncOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<template #extra-columns>
|
||||
<el-table-column label="微信上报状态" width="130">
|
||||
<template #default="{ row }"><el-tag :type="wechatSyncTag(row.wechatSyncStatus)">{{ wechatSyncLabel(row.wechatSyncStatus) }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="wechatSyncTime" label="最近上报时间" width="175" />
|
||||
<el-table-column prop="wechatSyncError" label="上报处理原因" min-width="230" show-overflow-tooltip />
|
||||
</template>
|
||||
<template #extra-actions="{ row }">
|
||||
<el-button link type="primary" @click="showLogs(row)">同步日志</el-button>
|
||||
<el-button link type="warning" :loading="retryingSn === row.sn" :disabled="!!retryingSn" @click="retry(row)">补报</el-button>
|
||||
</template>
|
||||
</OrderList>
|
||||
<SyncLogDrawer v-model="logVisible" :order-sn="logOrderSn" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import OrderList from "../order/orderList.vue";
|
||||
import SyncLogDrawer from "./SyncLogDrawer.vue";
|
||||
import { ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { retryWechatOrder } from "@/api/order";
|
||||
import { wechatSyncOptions, wechatSyncLabel, wechatSyncTag } from "@/utils/wechatOrderSync";
|
||||
|
||||
defineOptions({ name: "wechatOrderList" });
|
||||
|
||||
const orderListRef = ref(null);
|
||||
const retryingSn = ref("");
|
||||
const logVisible = ref(false);
|
||||
const logOrderSn = ref("");
|
||||
|
||||
/** 展示当前订单所在微信支付流水的审计日志。 */
|
||||
function showLogs(row) {
|
||||
logOrderSn.value = row.sn;
|
||||
logVisible.value = true;
|
||||
}
|
||||
|
||||
/** 人工补报需要明确确认范围;同笔支付共用锁,已成功且内容未变时由后端跳过。 */
|
||||
async function retry(row) {
|
||||
if (retryingSn.value) return;
|
||||
try {
|
||||
const historyNotice = row.wechatSyncStatus === "UNCHECKED" ? "该订单没有本地上报记录,请先核查微信后台,确认需要补报后继续。" : "";
|
||||
await ElMessageBox.confirm(historyNotice + "将按该笔微信支付的全部关联订单归集补报。人工补报不受自动上报开关限制;已成功且内容未变化会跳过。", "补报微信发货信息", { type: "warning", confirmButtonText: "确认补报", cancelButtonText: "取消" });
|
||||
} catch (e) { return; }
|
||||
if (retryingSn.value) return;
|
||||
retryingSn.value = row.sn;
|
||||
try {
|
||||
const res = await retryWechatOrder(row.sn);
|
||||
if (!res.success) throw new Error(res.message || "补报失败");
|
||||
if (res.result === "SUCCESS") ElMessage.success("微信接口已受理发货信息");
|
||||
else if (res.result === "SKIPPED") ElMessage.success("相同发货信息已成功上报,本次已跳过");
|
||||
else {
|
||||
ElMessage.warning("本次未完成上报,请查看同步日志中的处理原因");
|
||||
showLogs(row);
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error("补报未完成,请刷新状态并查看日志后再处理");
|
||||
} finally {
|
||||
retryingSn.value = "";
|
||||
orderListRef.value?.reload();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
74
manager/tests/wechatOrderSync.test.cjs
Normal file
74
manager/tests/wechatOrderSync.test.cjs
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 小程序订单筛选及异步竞态回归,隔离网络验证旧响应不覆盖新页面。
|
||||
* @author Bulbasaur
|
||||
* @since 2026-11-09
|
||||
*/
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
const { parse } = require('@vue/compiler-sfc');
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const tick = () => new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
/** 从实际 Vue 脚本加载逻辑,替换网络及 Vue 生命周期,不启动业务服务。 */
|
||||
function script(file) {
|
||||
return parse(fs.readFileSync(path.join(root,file),'utf8')).descriptor.scriptSetup.content.replace(/^import .*;\r?$/gm,'');
|
||||
}
|
||||
|
||||
/** 为并发请求提供可控完成顺序。 */
|
||||
function deferred() {
|
||||
let resolve, reject;
|
||||
const promise = new Promise((yes,no) => {resolve=yes; reject=no;});
|
||||
return {promise,resolve,reject};
|
||||
}
|
||||
|
||||
/** 保留表单值及计算属性的最小运行环境。 */
|
||||
function primitives() {
|
||||
return { ref:value=>({value}), computed:fn=>({get value(){return fn();}}), defineOptions:()=>{}, defineExpose:()=>{}, onMounted:()=>{} };
|
||||
}
|
||||
|
||||
test('专用列表固定支付范围、重置筛选,且旧列表和数量响应不能覆盖新结果', async()=>{
|
||||
const requests=[];
|
||||
const send=kind=>params=>{const wait=deferred();requests.push({kind,params:{...params},...wait});return wait.promise;};
|
||||
const context={...primitives(),defineProps:()=>({fixedClientType:'WECHAT_MP',fixedPaymentMethod:'WECHAT',wechatSync:true,allowExport:false}),useRoute:()=>({query:{clientType:'PC',paymentMethod:'WALLET'}}),watch:()=>{},wechatSyncOptions:[],API_Order:{getWechatOrderList:send('list'),getWechatOrderNum:send('count')}};
|
||||
vm.createContext(context);
|
||||
vm.runInContext(script('src/views/order/order/orderList.vue')+';globalThis.t={init,handleSearch,handleReset,changePage,orderStatusClick,searchForm,data,orderNumData,loading,loadError};',context);
|
||||
const t=context.t;
|
||||
t.init();
|
||||
t.searchForm.value.paymentMethod='WALLET';t.searchForm.value.wechatSyncStatus='FAILED';t.handleSearch();
|
||||
const newest=requests.filter(r=>r.kind==='list').at(-1);
|
||||
newest.resolve({success:true,result:{records:[{sn:'new'}],total:1}});
|
||||
requests.filter(r=>r.kind==='count').at(-1).resolve({success:true,result:{waitPayNum:5}});
|
||||
await tick();
|
||||
requests[0].resolve({success:true,result:{records:[{sn:'old'}],total:99}});
|
||||
requests[1].resolve({success:true,result:{waitPayNum:99}});
|
||||
await tick();
|
||||
assert.equal(t.data.value[0].sn,'new');assert.equal(t.orderNumData.value.waitPayNum,5);
|
||||
t.changePage(2);t.orderStatusClick('DELIVERED');t.handleReset();
|
||||
for(const r of requests){assert.equal(r.params.clientType,'WECHAT_MP');assert.equal(r.params.paymentMethod,'WECHAT');}
|
||||
assert.equal(t.searchForm.value.wechatSyncStatus,'');assert.equal(t.searchForm.value.paymentMethod,'WECHAT');
|
||||
requests.filter(r=>r.kind==='list').at(-1).reject(new Error('network'));
|
||||
await tick();assert.equal(t.loadError.value,true);assert.equal(t.data.value.length,0);assert.equal(t.loading.value,false);
|
||||
});
|
||||
|
||||
test('普通订单仍使用原接口且允许其他支付方式,不强制附加上报条件', async()=>{
|
||||
let sent;
|
||||
const context={...primitives(),defineProps:()=>({fixedClientType:'',fixedPaymentMethod:'',wechatSync:false,allowExport:true}),useRoute:()=>({query:{}}),watch:()=>{},wechatSyncOptions:[],API_Order:{getOrderList:params=>{sent={...params};return Promise.resolve({success:true,result:{records:[],total:0}});},getOrderNum:()=>Promise.resolve({success:true,result:{}})}};
|
||||
vm.createContext(context);vm.runInContext(script('src/views/order/order/orderList.vue')+';globalThis.t={init,searchForm};',context);
|
||||
context.t.searchForm.value.paymentMethod='WALLET';context.t.init();await tick();
|
||||
assert.equal(sent.paymentMethod,'WALLET');assert.equal(sent.clientType,undefined);assert.equal(sent.wechatSyncStatus,undefined);
|
||||
});
|
||||
|
||||
test('日志抽屉切换订单及关闭后,旧请求不会回填另一笔订单', async()=>{
|
||||
const props={modelValue:true,orderSn:'A'},requests=[];let changed;
|
||||
const context={...primitives(),defineProps:()=>props,defineEmits:()=>()=>{},watch:(source,callback)=>{changed=callback;callback();},getWechatOrderLogs:(sn,params)=>{const wait=deferred();requests.push({sn,...wait});return wait.promise;}};
|
||||
vm.createContext(context);vm.runInContext(script('src/views/order/wechat/SyncLogDrawer.vue')+';globalThis.t={records,loading,failed,load};',context);
|
||||
props.orderSn='B';changed();
|
||||
requests[1].resolve({success:true,result:{records:[{orderSn:'B'}],total:1}});await tick();
|
||||
requests[0].resolve({success:true,result:{records:[{orderSn:'A'}],total:1}});await tick();
|
||||
assert.equal(context.t.records.value[0].orderSn,'B');
|
||||
context.t.load();props.modelValue=false;changed();requests[2].resolve({success:true,result:{records:[{orderSn:'B'}],total:1}});await tick();
|
||||
assert.equal(context.t.records.value.length,0);assert.equal(context.t.loading.value,false);
|
||||
});
|
||||
Reference in New Issue
Block a user