| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- <template>
- <!-- 对账详情抽屉 -->
- <el-drawer v-model="drawer.visible" size="75%" direction="rtl" :before-close="handleDrawerClose" :close-on-click-modal="true">
- <template #header>
- <div class="drawer-header">
- <span class="order-title">对账详情</span>
- </div>
- </template>
- <div class="drawer-content">
- <el-form ref="formRef" :model="form" :rules="rules" label-width="135px">
- <!-- 基本信息 -->
- <el-divider content-position="left">
- <span style="color: #409eff; font-weight: 600">基本信息</span>
- </el-divider>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="对账单号:" prop="statementOrderNo">
- <el-input v-model="form.statementOrderNo" disabled />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="对账状态:" prop="statementStatus">
- <dict-tag :options="statement_status" :value="form.statementStatus" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="客户名称:" prop="customerName">
- <el-input v-model="form.customerName" disabled />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="对账单金额:" prop="amount">
- <el-input v-model="form.amount" disabled />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="对账人:" prop="statementSelf">
- <el-input v-model="form.statementSelf" disabled />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="对账人联系方式:" prop="statementSelfPhone">
- <el-input v-model="form.statementSelfPhone" placeholder="请填写对账人联系方式" disabled />
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="对账日期:" prop="statementDate">
- <el-date-picker
- v-model="form.statementDate"
- type="date"
- placeholder="请选择对账日期"
- style="width: 100%"
- value-format="YYYY-MM-DD"
- disabled
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="支付状态:" prop="isPaymentStatus">
- <el-select v-model="form.isPaymentStatus" placeholder="请选择支付状态" clearable style="width: 100%" disabled>
- <el-option v-for="dict in payment_status" :key="dict.value" :label="dict.label" :value="dict.value" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="发票状态:" prop="isInvoiceStatus">
- <el-select v-model="form.isInvoiceStatus" placeholder="请选择发票状态" clearable style="width: 100%" disabled>
- <el-option v-for="dict in invoice_issuance_status" :key="dict.value" :label="dict.label" :value="dict.value" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="24">
- <el-form-item label="备注:" prop="remark">
- <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" maxlength="500" show-word-limit disabled />
- </el-form-item>
- </el-col>
- </el-row>
- <!-- 对账明细 -->
- <el-divider content-position="left">
- <span style="color: #409eff; font-weight: 600">对账明细</span>
- </el-divider>
- <el-table :data="form.detailList" border style="width: 100%; margin-bottom: 20px">
- <el-table-column type="index" label="序号" width="60" align="center" />
- <el-table-column prop="orderNo" label="订单编号" min-width="150" align="center" />
- <el-table-column prop="amount" label="金额" min-width="120" align="center" />
- <el-table-column prop="orderTime" label="下单日期" min-width="120" align="center" />
- <el-table-column prop="userName" label="下单人" min-width="100" align="center" />
- <el-table-column prop="userDept" label="部门" min-width="120" align="center" />
- </el-table>
- <!--
- <div v-if="form.detailList.length === 0" class="empty-data">
- <el-empty description="暂无数据" :image-size="100" />
- </div> -->
- <!-- 商品清单 -->
- <el-divider content-position="left">
- <span style="color: #409eff; font-weight: 600">商品清单</span>
- </el-divider>
- <el-table :data="pagedProductList" border style="width: 100%; margin-bottom: 20px">
- <el-table-column
- type="index"
- label="序号"
- width="60"
- align="center"
- :index="(index) => (productPage.pageNum - 1) * productPage.pageSize + index + 1"
- />
- <el-table-column prop="orderNo" label="订单编号" min-width="120" align="center" />
- <el-table-column prop="productNo" label="商品编号" min-width="120" align="center" />
- <el-table-column prop="itemName" label="商品名称" min-width="180" align="center" />
- <el-table-column prop="unitName" label="单位" width="80" align="center" />
- <el-table-column prop="quantity" label="数量" width="100" align="center" />
- <el-table-column prop="unitPrice" label="单价" width="100" align="center">
- <template #default="scope">
- {{ scope.row.unitPrice ? Number(scope.row.unitPrice).toFixed(2) : '0.00' }}
- </template>
- </el-table-column>
- <el-table-column label="小计" width="120" align="center">
- <template #default="scope">
- {{ (Number(scope.row.quantity || 0) * Number(scope.row.unitPrice || 0)).toFixed(2) }}
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <el-pagination
- v-if="form.productList.length > 0"
- v-model:current-page="productPage.pageNum"
- v-model:page-size="productPage.pageSize"
- :page-sizes="[10, 20, 50, 100]"
- layout="total, sizes, prev, pager, next, jumpers"
- :total="productPage.total"
- @size-change="handleProductSizeChange"
- @current-change="handleProductCurrentChange"
- style="margin-top: 15px"
- />
- <!-- 对账附件 -->
- <el-divider content-position="left">
- <span style="color: #409eff; font-weight: 600">对账附件</span>
- </el-divider>
- <el-table :data="fileList" border style="width: 100%; margin-bottom: 20px">
- <el-table-column type="index" label="序号" width="80" align="center" />
- <el-table-column prop="name" label="文件名称" min-width="200" align="center" />
- <el-table-column prop="url" label="文件地址" min-width="250" align="center">
- <template #default="scope">
- {{ scope.row.url || '暂无地址' }}
- </template>
- </el-table-column>
- <el-table-column label="操作" width="160" align="center">
- <template #default="scope">
- <el-button type="primary" link @click="handleDownloadFile(scope.row)">下载</el-button>
- <el-button type="primary" link @click="handlePreviewFile(scope.row)">预览</el-button>
- </template>
- </el-table-column>
- </el-table>
- <!--
- <div v-if="fileList.length === 0" class="empty-data">
- <el-empty description="暂无附件" :image-size="100" />
- </div> -->
- </el-form>
- </div>
- <template #footer>
- <div class="drawer-footer">
- <el-button @click="handleDrawerClose(() => (drawer.visible = false))">取消</el-button>
- <el-button type="primary" :loading="buttonLoading" @click="handleDrawerClose(() => (drawer.visible = false))">提交</el-button>
- </div>
- </template>
- </el-drawer>
- </template>
- <script setup name="AddDrawer" lang="ts">
- import { addStatementOrder, updateStatementOrder } from '@/api/bill/statementOrder';
- import { StatementOrderForm } from '@/api/bill/statementOrder/types';
- import { CustomerInfoVO } from '@/api/customer/customerFile/customerInfo/types';
- import { listOrderMain, getOrderMain } from '@/api/order/orderMain';
- import { OrderMainVO, OrderMainQuery, OrderMainForm } from '@/api/order/orderMain/types';
- const { proxy } = getCurrentInstance() as ComponentInternalInstance;
- const { statement_status, invoice_issuance_status, payment_status } = toRefs<any>(
- proxy?.useDict('statement_status', 'statement_order_type', 'invoice_issuance_status', 'payment_status')
- );
- const initFormData: StatementOrderForm = {
- id: undefined,
- statementOrderNo: undefined,
- customerId: undefined,
- customerNo: undefined,
- customerName: undefined,
- amount: undefined,
- statementSelfId: undefined,
- statementSelf: undefined,
- statementSelfPhone: undefined,
- statementStatus: undefined,
- isPaymentStatus: undefined,
- isInvoiceStatus: undefined,
- statementDate: undefined,
- annexAddress: undefined,
- rejectRemark: undefined,
- remark: undefined,
- detailList: [],
- productList: []
- };
- const emit = defineEmits(['success']);
- const formRef = ref<any>();
- const buttonLoading = ref(false);
- const form = ref<
- StatementOrderForm & {
- actualAmount?: string;
- accountingVerification?: string;
- skuProductName?: string;
- skuProductModel?: string;
- attachmentRemark?: string;
- }
- >({ ...initFormData });
- const rules = reactive({
- customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
- statementSelfId: [{ required: true, message: '请选择对账人', trigger: 'blur' }],
- statementSelfPhone: [
- { required: true, message: '请输入对账人手机号', trigger: 'blur' },
- { pattern: /^1[3-9]\d{9}$/ as RegExp, message: '请输入正确的手机号格式', trigger: 'blur' }
- ]
- } as any);
- const fileList = ref<any[]>([]);
- const productPage = reactive({
- pageNum: 1,
- pageSize: 10,
- total: 0
- });
- const drawer = reactive<DialogOption>({
- visible: false,
- title: '对账详情'
- });
- const isEdit = ref(false); // 是否为编辑模式
- const customerOptions = ref<CustomerInfoVO[]>([]);
- const preloadedOrders = ref<OrderMainVO[]>([]); // 预加载的订单列表
- const preloadedTotal = ref(0); // 预加载的订单总数
- /** 计算当前页的商品列表 */
- const pagedProductList = computed(() => {
- const start = (productPage.pageNum - 1) * productPage.pageSize;
- const end = start + productPage.pageSize;
- return form.value.productList?.slice(start, end) || [];
- });
- /** 打开新增/编辑对账详情抽屉 */
- const open = (id?: string | number, data?: StatementOrderForm) => {
- reset();
- if (id && data) {
- // 查看模式
- drawer.title = '对账详情';
- Object.assign(form.value, data);
- // 将客户信息添加到下拉选项中,以便回显
- if (data.customerId && data.customerName) {
- customerOptions.value = [
- {
- id: data.customerId,
- customerName: data.customerName,
- customerNo: data.customerNo || ''
- } as CustomerInfoVO
- ];
- }
- // 解析附件地址并回显附件列表
- if (data.annexAddress) {
- const urls = data.annexAddress.split(',').filter((url) => url.trim());
- fileList.value = urls.map((url, index) => {
- const fileName = url.split('/').pop() || `附件${index + 1}`;
- return {
- name: fileName,
- url: url.trim(),
- id: undefined
- };
- });
- }
- // 设置商品列表分页总数
- if (data.productList && data.productList.length > 0) {
- productPage.total = data.productList.length;
- }
- }
- // 先打开抽屉
- drawer.visible = true;
- };
- /** 表单重置 */
- const reset = () => {
- form.value = { ...initFormData };
- fileList.value = [];
- productPage.pageNum = 1;
- productPage.pageSize = 10;
- productPage.total = 0;
- customerOptions.value = [];
- formRef.value?.clearValidate();
- };
- /** 预加载订单列表 */
- const preloadOrders = async (customerId: string | number) => {
- try {
- const params: OrderMainQuery = {
- pageNum: 1,
- pageSize: 10,
- customerId: customerId
- };
- const res = await listOrderMain(params);
- preloadedOrders.value = res.rows || [];
- preloadedTotal.value = res.total || 0;
- } catch (error) {
- console.error('预加载订单列表失败:', error);
- preloadedOrders.value = [];
- preloadedTotal.value = 0;
- }
- };
- /** 产品分页大小改变 */
- const handleProductSizeChange = (val: number) => {
- productPage.pageSize = val;
- };
- /** 产品分页页码改变 */
- const handleProductCurrentChange = (val: number) => {
- productPage.pageNum = val;
- };
- /** 下载文件 */
- const handleDownloadFile = async (file: any) => {
- if (!file.url) {
- proxy?.$modal.msgWarning('文件地址不存在');
- return;
- }
- try {
- // 使用fetch获取文件
- const response = await fetch(file.url);
- const blob = await response.blob();
- // 创建Blob URL
- const blobUrl = window.URL.createObjectURL(blob);
- // 创建下载链接
- const link = document.createElement('a');
- link.href = blobUrl;
- link.download = file.name || '附件';
- document.body.appendChild(link);
- link.click();
- // 清理
- document.body.removeChild(link);
- window.URL.revokeObjectURL(blobUrl);
- } catch (error) {
- console.error('下载失败:', error);
- // 如果fetch失败,回退到直接打开链接的方式
- const link = document.createElement('a');
- link.href = file.url;
- link.download = file.name || '附件';
- link.target = '_blank';
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- }
- };
- /** 预览文件 */
- const handlePreviewFile = (file: any) => {
- if (!file.url) {
- proxy?.$modal.msgWarning('文件地址不存在');
- return;
- }
- // 在新窗口打开文件
- window.open(file.url, '_blank');
- };
- /** 提交表单 */
- const handleSubmit = async () => {
- if (!formRef.value) return;
- await formRef.value.validate(async (valid) => {
- if (valid) {
- buttonLoading.value = true;
- try {
- if (isEdit.value) {
- // 编辑模式
- await updateStatementOrder(form.value);
- proxy?.$modal.msgSuccess('修改成功');
- } else {
- // 新增模式
- await addStatementOrder(form.value);
- proxy?.$modal.msgSuccess('新增成功');
- }
- drawer.visible = false;
- emit('success');
- } catch (error) {
- console.error(error);
- } finally {
- buttonLoading.value = false;
- }
- }
- });
- };
- /** 关闭抽屉前的回调 */
- const handleDrawerClose = (done: () => void) => {
- if (buttonLoading.value) {
- return;
- }
- done();
- reset();
- };
- defineExpose({
- open
- });
- </script>
- <style scoped lang="scss">
- .drawer-header {
- display: flex;
- align-items: center;
- gap: 10px;
- .order-title {
- font-size: 16px;
- font-weight: 600;
- color: #303133;
- }
- }
- .drawer-content {
- padding: 0 20px 20px;
- height: calc(100% - 60px);
- overflow-y: auto;
- }
- .assign-dialog-content {
- padding: 0 10px;
- }
- .assign-target-section {
- margin-bottom: 20px;
- .target-input-wrapper {
- display: flex;
- align-items: center;
- gap: 8px;
- .required-mark {
- color: #f56c6c;
- font-size: 14px;
- }
- .label {
- font-size: 14px;
- color: #606266;
- white-space: nowrap;
- }
- }
- .or-divider {
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- color: #909399;
- }
- }
- .product-list-section {
- margin-top: 20px;
- }
- .detail-header {
- padding: 15px 20px;
- background-color: #f5f7fa;
- border-radius: 4px;
- margin-bottom: 20px;
- .info-item {
- display: flex;
- flex-direction: column;
- gap: 5px;
- .label {
- font-size: 12px;
- color: #909399;
- }
- .value {
- font-size: 14px;
- color: #303133;
- font-weight: 500;
- }
- }
- }
- .detail-tabs {
- .tab-actions {
- margin-bottom: 15px;
- }
- }
- :deep(.el-alert__title) {
- font-size: 13px;
- }
- .empty-data {
- padding: 20px 0;
- text-align: center;
- }
- .drawer-footer {
- display: flex;
- justify-content: flex-end;
- gap: 10px;
- padding: 15px 20px;
- border-top: 1px solid #e4e7ed;
- }
- :deep(.el-upload__tip) {
- margin-top: 8px;
- font-size: 12px;
- color: #909399;
- }
- </style>
|