|
|
@@ -0,0 +1,322 @@
|
|
|
+<!-- 服务商管理 -->
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <PageSearch
|
|
|
+ ref="searchRef"
|
|
|
+ :search-config="searchConfig"
|
|
|
+ @query-click="handleQueryClick"
|
|
|
+ @reset-click="handleResetClick"
|
|
|
+ />
|
|
|
+
|
|
|
+ <PageContent ref="contentRef" :content-config="contentConfig">
|
|
|
+ <template #toolbar="{ toolbarRight, onToolbar, removeIds, cols }">
|
|
|
+ <CrudToolbarLeft
|
|
|
+ :remove-ids="removeIds"
|
|
|
+ :perm-create="['module_system:service_provider:create']"
|
|
|
+ :perm-delete="['module_system:service_provider:delete']"
|
|
|
+ @add="handleOpenDialog('create')"
|
|
|
+ @delete="onToolbar('delete')"
|
|
|
+ />
|
|
|
+ <div class="data-table__toolbar--right">
|
|
|
+ <CrudToolbarRight :buttons="toolbarRight" :cols="cols" :on-toolbar="onToolbar" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #table="{ data, loading, tableRef, onSelectionChange, pagination }">
|
|
|
+ <div class="data-table__content">
|
|
|
+ <el-table
|
|
|
+ :ref="tableRef as any"
|
|
|
+ v-loading="loading"
|
|
|
+ row-key="id"
|
|
|
+ :data="data"
|
|
|
+ height="100%"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ @selection-change="onSelectionChange"
|
|
|
+ >
|
|
|
+ <template #empty>
|
|
|
+ <el-empty :image-size="80" description="暂无数据" />
|
|
|
+ </template>
|
|
|
+ <el-table-column type="selection" min-width="55" align="center" />
|
|
|
+ <el-table-column label="序号" min-width="60" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ (pagination.currentPage - 1) * pagination.pageSize + scope.$index + 1 }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="服务商名称" prop="name" min-width="140" show-overflow-tooltip />
|
|
|
+ <el-table-column label="业务范围" prop="scope_label" min-width="100" align="center" show-overflow-tooltip />
|
|
|
+ <el-table-column label="AppId" prop="app_id" min-width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column label="网关地址" prop="server_url" min-width="220" show-overflow-tooltip />
|
|
|
+ <el-table-column label="状态" prop="provider_status" min-width="80" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="scope.row.provider_status === 'ACTIVE' ? 'success' : 'danger'">
|
|
|
+ {{ scope.row.provider_status === "ACTIVE" ? "启用" : "禁用" }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="创建时间" prop="created_time" min-width="180" show-overflow-tooltip />
|
|
|
+ <el-table-column fixed="right" label="操作" align="center" min-width="220">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button
|
|
|
+ v-hasPerm="['module_system:service_provider:detail']"
|
|
|
+ type="info" size="small" link icon="View"
|
|
|
+ @click="handleOpenDialog('detail', scope.row.id)"
|
|
|
+ >详情</el-button>
|
|
|
+ <el-button
|
|
|
+ v-hasPerm="['module_system:service_provider:update']"
|
|
|
+ type="primary" size="small" link icon="edit"
|
|
|
+ @click="handleOpenDialog('update', scope.row.id)"
|
|
|
+ >编辑</el-button>
|
|
|
+ <el-button
|
|
|
+ v-hasPerm="['module_system:service_provider:update']"
|
|
|
+ type="warning" size="small" link icon="SwitchButton"
|
|
|
+ @click="handleToggle(scope.row.id, scope.row.provider_status)"
|
|
|
+ >{{ scope.row.provider_status === 'ACTIVE' ? '停用' : '启用' }}</el-button>
|
|
|
+ <el-button
|
|
|
+ v-hasPerm="['module_system:service_provider:delete']"
|
|
|
+ type="danger" size="small" link icon="delete"
|
|
|
+ @click="handleRowDelete(scope.row.id)"
|
|
|
+ >删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </PageContent>
|
|
|
+
|
|
|
+ <EnhancedDialog v-model="dialogVisible.visible" :title="dialogVisible.title" @close="handleCloseDialog">
|
|
|
+ <!-- 详情 -->
|
|
|
+ <template v-if="dialogVisible.type === 'detail'">
|
|
|
+ <el-descriptions :column="2" border>
|
|
|
+ <el-descriptions-item label="服务商名称">{{ detailData.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="业务范围">{{ detailData.scope_label }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="AppId">{{ detailData.app_id }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="网关地址" :span="2">{{ detailData.server_url }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="密钥片段(App)" :span="2">{{ detailData.app_private_key_hint || '****' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="密钥片段(支付宝)" :span="2">{{ detailData.alipay_public_key_hint || '****' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="签名方式">{{ detailData.sign_type }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="格式">{{ detailData.format }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="编码">{{ detailData.charset }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="状态">
|
|
|
+ <el-tag :type="detailData.provider_status === 'ACTIVE' ? 'success' : 'danger'">
|
|
|
+ {{ detailData.provider_status === "ACTIVE" ? "启用" : "禁用" }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="描述" :span="2">{{ detailData.description || '-' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">{{ detailData.created_time }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="更新时间">{{ detailData.updated_time }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 新增 -->
|
|
|
+ <template v-else-if="dialogVisible.type === 'create'">
|
|
|
+ <el-form ref="dataFormRef" :model="formData" :rules="rules" label-suffix=":" label-width="auto" label-position="right">
|
|
|
+ <el-form-item label="服务商名称" prop="name">
|
|
|
+ <el-input v-model="formData.name" placeholder="请输入名称" :maxlength="64" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="业务范围标签" prop="scope_label">
|
|
|
+ <el-input v-model="formData.scope_label" placeholder="如 DOMESTIC / OVERSEAS" :maxlength="64" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="AppId" prop="app_id">
|
|
|
+ <el-input v-model="formData.app_id" placeholder="支付宝应用 ID" :maxlength="64" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="应用私钥" prop="app_private_key">
|
|
|
+ <el-input v-model="formData.app_private_key" type="textarea" :rows="4" placeholder="粘贴 RSA2 私钥" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="支付宝公钥" prop="alipay_public_key">
|
|
|
+ <el-input v-model="formData.alipay_public_key" type="textarea" :rows="4" placeholder="粘贴支付宝 RSA2 公钥" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="网关地址" prop="server_url">
|
|
|
+ <el-input v-model="formData.server_url" placeholder="https://openapi.alipay.com/gateway.do" :maxlength="256" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="签名方式" prop="sign_type">
|
|
|
+ <el-select v-model="formData.sign_type" style="width: 100%">
|
|
|
+ <el-option label="RSA2" value="RSA2" />
|
|
|
+ <el-option label="RSA" value="RSA" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="描述" prop="description">
|
|
|
+ <el-input v-model="formData.description" type="textarea" :rows="2" placeholder="可选" :maxlength="512" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 编辑(仅名称 + 业务范围) -->
|
|
|
+ <template v-else-if="dialogVisible.type === 'update'">
|
|
|
+ <el-form ref="dataFormRef" :model="formData" :rules="updateRules" label-suffix=":" label-width="auto" label-position="right">
|
|
|
+ <el-form-item label="服务商名称" prop="name">
|
|
|
+ <el-input v-model="formData.name" placeholder="请输入名称" :maxlength="64" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="业务范围标签" prop="scope_label">
|
|
|
+ <el-input v-model="formData.scope_label" placeholder="如 DOMESTIC / OVERSEAS" :maxlength="64" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="handleCloseDialog">取消</el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="dialogVisible.type !== 'detail'"
|
|
|
+ v-hasPerm="['module_system:service_provider:' + (dialogVisible.type === 'create' ? 'create' : 'update')]"
|
|
|
+ type="primary"
|
|
|
+ :loading="submitLoading"
|
|
|
+ @click="handleSubmit"
|
|
|
+ >确定</el-button>
|
|
|
+ <el-button v-else type="primary" @click="handleCloseDialog">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </EnhancedDialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive } from "vue";
|
|
|
+import ProviderAPI, {
|
|
|
+ type ServiceProviderTable,
|
|
|
+ type ServiceProviderCreateForm,
|
|
|
+ type ServiceProviderUpdateForm,
|
|
|
+} from "@/api/module_system/service_provider";
|
|
|
+import CrudToolbarLeft from "@/components/CURD/CrudToolbarLeft.vue";
|
|
|
+import CrudToolbarRight from "@/components/CURD/CrudToolbarRight.vue";
|
|
|
+import PageSearch from "@/components/CURD/PageSearch.vue";
|
|
|
+import PageContent from "@/components/CURD/PageContent.vue";
|
|
|
+import EnhancedDialog from "@/components/CURD/EnhancedDialog.vue";
|
|
|
+import { useCrudList } from "@/components/CURD/useCrudList";
|
|
|
+import type { IContentConfig, ISearchConfig } from "@/components/CURD/types";
|
|
|
+import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
+
|
|
|
+defineOptions({ name: "ServiceProvider", inheritAttrs: false });
|
|
|
+
|
|
|
+const { searchRef, contentRef, handleQueryClick, handleResetClick, refreshList } = useCrudList();
|
|
|
+const dataFormRef = ref();
|
|
|
+const submitLoading = ref(false);
|
|
|
+
|
|
|
+const searchConfig = reactive<ISearchConfig>({
|
|
|
+ permPrefix: "module_system:service_provider",
|
|
|
+ colon: true,
|
|
|
+ isExpandable: true,
|
|
|
+ showNumber: 2,
|
|
|
+ form: { labelWidth: "auto" },
|
|
|
+ formItems: [
|
|
|
+ { prop: "name", label: "服务商名称", type: "input", attrs: { placeholder: "请输入名称", clearable: true } },
|
|
|
+ { prop: "scope_label", label: "业务范围", type: "input", attrs: { placeholder: "如 DOMESTIC", clearable: true } },
|
|
|
+ { prop: "status", label: "状态", type: "select",
|
|
|
+ attrs: { placeholder: "请选择", clearable: true, style: { width: "167px" },
|
|
|
+ options: [{ label: "启用", value: "ACTIVE" }, { label: "禁用", value: "DISABLED" }] } },
|
|
|
+ ],
|
|
|
+});
|
|
|
+
|
|
|
+const contentConfig = reactive<IContentConfig<any>>({
|
|
|
+ permPrefix: "module_system:service_provider",
|
|
|
+ pk: "id",
|
|
|
+ cols: [
|
|
|
+ { prop: "selection", label: "选择框", show: true },
|
|
|
+ { prop: "index", label: "序号", show: true },
|
|
|
+ { prop: "name", label: "名称", show: true },
|
|
|
+ { prop: "scope_label", label: "业务范围", show: true },
|
|
|
+ { prop: "app_id", label: "AppId", show: true },
|
|
|
+ { prop: "server_url", label: "网关地址", show: true },
|
|
|
+ { prop: "provider_status", label: "状态", show: true },
|
|
|
+ { prop: "created_time", label: "创建时间", show: true },
|
|
|
+ { prop: "operation", label: "操作", show: true },
|
|
|
+ ] as any,
|
|
|
+ hideColumnFilter: false,
|
|
|
+ toolbar: [],
|
|
|
+ defaultToolbar: [{ name: "refresh", perm: "refresh" }, "filter"],
|
|
|
+ pagination: { pageSize: 10, pageSizes: [10, 20, 30, 50] },
|
|
|
+ request: { page_no: "page_no", page_size: "page_size" },
|
|
|
+ indexAction: async (params) => {
|
|
|
+ const res = await ProviderAPI.list(params);
|
|
|
+ return { total: res.data.data.total, list: res.data.data.items };
|
|
|
+ },
|
|
|
+ deleteAction: async (ids) => {
|
|
|
+ const idList = ids.split(",").map((s: string) => Number(s.trim())).filter((n: number) => !Number.isNaN(n));
|
|
|
+ await ProviderAPI.delete(idList);
|
|
|
+ },
|
|
|
+ deleteConfirm: { title: "警告", message: "确认删除该服务商?", type: "warning" },
|
|
|
+});
|
|
|
+
|
|
|
+const detailData = ref<ServiceProviderTable>({});
|
|
|
+const currentEditId = ref<number | null>(null);
|
|
|
+
|
|
|
+const formData = reactive({
|
|
|
+ name: "", scope_label: "DOMESTIC", app_id: "", app_private_key: "", alipay_public_key: "",
|
|
|
+ server_url: "https://openapi.alipay.com/gateway.do", sign_type: "RSA2", format: "JSON", charset: "UTF-8", description: "",
|
|
|
+});
|
|
|
+
|
|
|
+const dialogVisible = reactive({ title: "", visible: false, type: "create" as "create" | "update" | "detail" });
|
|
|
+
|
|
|
+const rules = reactive({
|
|
|
+ name: [{ required: true, message: "请输入名称", trigger: "blur" }],
|
|
|
+ scope_label: [{ required: true, message: "请输入业务范围", trigger: "blur" }],
|
|
|
+ app_id: [{ required: true, message: "请输入 AppId", trigger: "blur" }],
|
|
|
+ app_private_key: [{ required: true, message: "请输入应用私钥", trigger: "blur" }],
|
|
|
+ alipay_public_key: [{ required: true, message: "请输入支付宝公钥", trigger: "blur" }],
|
|
|
+});
|
|
|
+
|
|
|
+const updateRules = reactive({
|
|
|
+ name: [{ required: true, message: "请输入名称", trigger: "blur" }],
|
|
|
+ scope_label: [{ required: true, message: "请输入业务范围", trigger: "blur" }],
|
|
|
+});
|
|
|
+
|
|
|
+function handleRowDelete(id: number) { contentRef.value?.handleDelete(id); }
|
|
|
+
|
|
|
+async function handleToggle(id: number, currentStatus: string) {
|
|
|
+ const action = currentStatus === "ACTIVE" ? "停用" : "启用";
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(`确认${action}该服务商?`, "提示", { confirmButtonText: action, cancelButtonText: "取消", type: "warning" });
|
|
|
+ const res = await ProviderAPI.toggle(id);
|
|
|
+ ElMessage.success(res.data.data?.provider_status === "ACTIVE" ? "已启用" : "已停用");
|
|
|
+ refreshList();
|
|
|
+ } catch { /* 用户取消或失败 */ }
|
|
|
+}
|
|
|
+
|
|
|
+async function handleCloseDialog() {
|
|
|
+ dialogVisible.visible = false;
|
|
|
+ dataFormRef.value?.resetFields();
|
|
|
+ dataFormRef.value?.clearValidate();
|
|
|
+ Object.assign(formData, { name: "", scope_label: "DOMESTIC", app_id: "", app_private_key: "", alipay_public_key: "", server_url: "https://openapi.alipay.com/gateway.do", sign_type: "RSA2", format: "JSON", charset: "UTF-8", description: "" });
|
|
|
+ currentEditId.value = null;
|
|
|
+}
|
|
|
+
|
|
|
+async function handleOpenDialog(type: "create" | "update" | "detail", id?: number) {
|
|
|
+ dialogVisible.type = type;
|
|
|
+ if (id) {
|
|
|
+ const response = await ProviderAPI.detail(id);
|
|
|
+ if (type === "detail") {
|
|
|
+ dialogVisible.title = "服务商详情";
|
|
|
+ Object.assign(detailData.value, response.data.data);
|
|
|
+ } else if (type === "update") {
|
|
|
+ dialogVisible.title = "编辑服务商";
|
|
|
+ Object.assign(formData, response.data.data);
|
|
|
+ currentEditId.value = id;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dialogVisible.title = "新增服务商";
|
|
|
+ }
|
|
|
+ dialogVisible.visible = true;
|
|
|
+}
|
|
|
+
|
|
|
+async function handleSubmit() {
|
|
|
+ dataFormRef.value.validate(async (valid: boolean) => {
|
|
|
+ if (!valid) return;
|
|
|
+ submitLoading.value = true;
|
|
|
+ try {
|
|
|
+ const id = currentEditId.value;
|
|
|
+ if (id) {
|
|
|
+ await ProviderAPI.update(id, formData as ServiceProviderUpdateForm);
|
|
|
+ } else {
|
|
|
+ await ProviderAPI.create(formData);
|
|
|
+ }
|
|
|
+ dialogVisible.visible = false;
|
|
|
+ await handleCloseDialog();
|
|
|
+ refreshList();
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message || "操作失败");
|
|
|
+ } finally {
|
|
|
+ submitLoading.value = false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+</script>
|