Prechádzať zdrojové kódy

1、租户管理调价样式优化

yys 3 dní pred
rodič
commit
bfaa9e16b9
1 zmenil súbory, kde vykonal 249 pridanie a 42 odobranie
  1. 249 42
      src/views/admin/sysCompany/index.vue

+ 249 - 42
src/views/admin/sysCompany/index.vue

@@ -303,41 +303,89 @@
     </el-dialog>
 
     <!-- ===== 模块定价弹窗 ===== -->
-    <el-dialog :title="'模块定价 - ' + pricingDialog.tenantName" :visible.sync="pricingDialog.visible" width="800px" append-to-body destroy-on-close>
-      <div v-loading="pricingDialog.loading">
-        <el-table :data="pricingDialog.modules" border size="small" style="width:100%">
-          <el-table-column label="模块" align="center" prop="moduleName" min-width="120" />
-          <el-table-column label="全局售价" align="center" min-width="100">
-            <template slot-scope="scope">
-              <span style="color:#909399">{{ scope.row.globalPrice != null ? scope.row.globalPrice : '-' }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="全局成本" align="center" min-width="100">
-            <template slot-scope="scope">
-              <span style="color:#909399">{{ scope.row.globalCost != null ? scope.row.globalCost : '-' }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="租户售价" align="center" min-width="130">
-            <template slot-scope="scope">
-              <el-input-number v-model="scope.row.price" :precision="4" :min="0" :step="0.01" size="small" style="width:150px" placeholder="跟随全局" />
-            </template>
-          </el-table-column>
-          <el-table-column label="成本价" align="center" min-width="130">
-            <template slot-scope="scope">
-              <el-input-number v-model="scope.row.costPrice" :precision="4" :min="0" :step="0.01" size="small" style="width:150px" placeholder="跟随全局" />
-            </template>
-          </el-table-column>
-          <el-table-column label="状态" align="center" min-width="80">
-            <template slot-scope="scope">
-              <el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0" active-text="启用" inactive-text="禁" size="small" />
-            </template>
-          </el-table-column>
-        </el-table>
-        <div style="color:#909399;font-size:12px;margin-top:8px">* 租户售价/成本价留空或填0则使用全局定价</div>
+    <el-dialog :title="'模块定价 - ' + pricingDialog.tenantName" :visible.sync="pricingDialog.visible" width="720px" append-to-body destroy-on-close class="pricing-dialog">
+      <div v-loading="pricingDialog.loading" class="pricing-container">
+        <!-- 头部提示 -->
+        <div class="pricing-header">
+          <i class="el-icon-info" style="color:#409EFF;margin-right:6px"></i>
+          <span>租户售价/成本价留空或填0则使用全局定价</span>
+        </div>
+
+        <!-- 模块卡片列表 -->
+        <div class="pricing-list">
+          <div v-for="(item, index) in pricingDialog.modules" :key="item.serviceType" class="pricing-card">
+            <!-- 卡片头部 -->
+            <div class="pricing-card-header">
+              <div class="module-title">
+                <el-tag size="small" type="primary" effect="plain">{{ item.moduleName }}</el-tag>
+              </div>
+              <el-switch
+                v-model="item.status"
+                :active-value="1"
+                :inactive-value="0"
+                active-text="启用"
+                inactive-text="禁用"
+                size="small"
+              />
+            </div>
+
+            <!-- 定价区域 -->
+            <div class="pricing-card-body">
+              <!-- 全局定价参考 -->
+              <div class="global-pricing">
+                <div class="price-item">
+                  <span class="price-label">全局售价</span>
+                  <span class="price-value global">¥{{ item.globalPrice != null ? item.globalPrice : '-' }}</span>
+                </div>
+                <div class="price-item">
+                  <span class="price-label">全局成本</span>
+                  <span class="price-value global">¥{{ item.globalCost != null ? item.globalCost : '-' }}</span>
+                </div>
+              </div>
+
+              <div class="divider"></div>
+
+              <!-- 租户自定义定价 -->
+              <div class="tenant-pricing">
+                <div class="price-item">
+                  <span class="price-label">
+                    <i class="el-icon-price-tag" style="color:#67C23A;margin-right:4px"></i>租户售价
+                  </span>
+                  <el-input-number
+                    v-model="item.price"
+                    :precision="4"
+                    :min="0"
+                    :step="0.01"
+                    size="small"
+                    controls-position="right"
+                    style="width:140px"
+                    placeholder="跟随全局"
+                  />
+                </div>
+                <div class="price-item">
+                  <span class="price-label">
+                    <i class="el-icon-coin" style="color:#E6A23C;margin-right:4px"></i>租户成本
+                  </span>
+                  <el-input-number
+                    v-model="item.costPrice"
+                    :precision="4"
+                    :min="0"
+                    :step="0.01"
+                    size="small"
+                    controls-position="right"
+                    style="width:140px"
+                    placeholder="跟随全局"
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
       </div>
-      <div slot="footer">
-        <el-button @click="pricingDialog.visible = false">取 消</el-button>
-        <el-button type="primary" :loading="pricingDialog.submitting" @click="submitModulePricing">保 存</el-button>
+
+      <div slot="footer" class="pricing-footer">
+        <el-button @click="pricingDialog.visible = false" size="medium">取 消</el-button>
+        <el-button type="primary" :loading="pricingDialog.submitting" size="medium" icon="el-icon-check" @click="submitModulePricing">保 存</el-button>
       </div>
     </el-dialog>
   </div>
@@ -361,7 +409,7 @@ export default {
       companyList: [],
       statusOptions: [
         { value: 1, label: '正常' },
-        { value: 0, label: '禁用' }
+        { value: 3, label: '禁用' }
       ],
       queryParams: {
         pageNum: 1,
@@ -630,7 +678,16 @@ export default {
     handleExport() {
       this.exportLoading = true
       exportCompany(this.queryParams).then(response => {
-        this.download(response.msg)
+        // 直接处理 Blob 下载
+        const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
+        const url = window.URL.createObjectURL(blob)
+        const link = document.createElement('a')
+        link.href = url
+        link.download = '租户列表数据.xlsx'
+        document.body.appendChild(link)
+        link.click()
+        document.body.removeChild(link)
+        window.URL.revokeObjectURL(url)
         this.exportLoading = false
       }).catch(() => { this.exportLoading = false })
     },
@@ -814,14 +871,17 @@ export default {
         tenantPricings.forEach(p => { pricingMap[p.serviceType] = p })
         this.pricingDialog.modules = costList.map(c => {
           const existing = pricingMap[c.serviceType]
+          // 如果租户定价为空或为0,则使用全局定价作为默认值
+          const tenantPrice = existing ? existing.price : null
+          const tenantCostPrice = existing ? existing.costPrice : null
           return {
             serviceType: c.serviceType,
             moduleName: c.configName,
             unit: c.feeUnit,
             globalPrice: c.feeStandard,
             globalCost: c.platformCost,
-            price: existing ? existing.price : null,
-            costPrice: existing ? existing.costPrice : null,
+            price: tenantPrice || c.feeStandard,
+            costPrice: tenantCostPrice || c.platformCost,
             status: existing ? existing.status : 1,
             id: existing ? existing.id : null
           }
@@ -881,14 +941,17 @@ export default {
         tenantPricings.forEach(p => { pricingMap[p.serviceType] = p })
         this.pricingDialog.modules = costList.map(c => {
           const existing = pricingMap[c.serviceType]
+          // 如果租户定价为空或为0,则使用全局定价作为默认值
+          const tenantPrice = existing ? existing.price : null
+          const tenantCostPrice = existing ? existing.costPrice : null
           return {
             serviceType: c.serviceType,
             moduleName: c.configName,
             unit: c.feeUnit,
             globalPrice: c.feeStandard,
             globalCost: c.platformCost,
-            price: existing ? existing.price : null,
-            costPrice: existing ? existing.costPrice : null,
+            price: tenantPrice || c.feeStandard,
+            costPrice: tenantCostPrice || c.platformCost,
             status: existing ? existing.status : 1,
             id: existing ? existing.id : null
           }
@@ -904,11 +967,18 @@ export default {
       this.pricingDialog.submitting = true
       const tenantId = this.pricingDialog.tenantId
       const promises = this.pricingDialog.modules.map(m => {
+        // 空值转为 0(使用全局定价)
+        const price = m.price || 0
+        const costPrice = m.costPrice || 0
+        // 从未设置过且都是 0,跳过
+        if (!m.id && price === 0 && costPrice === 0) {
+          return Promise.resolve()
+        }
         const data = {
           tenantId: tenantId,
           serviceType: m.serviceType,
-          price: m.price,
-          costPrice: m.costPrice,
+          price: price,
+          costPrice: costPrice,
           status: m.status
         }
         if (m.id) {
@@ -1018,4 +1088,141 @@ export default {
 .tenant-status-item ::v-deep .el-form-item__content {
   line-height: 32px;
 }
+
+/* ===== 模块定价弹窗样式 ===== */
+.pricing-dialog ::v-deep .el-dialog {
+  border-radius: 12px;
+  overflow: hidden;
+}
+.pricing-dialog ::v-deep .el-dialog__header {
+  background: linear-gradient(135deg, #409EFF 0%, #317ecc 100%);
+  padding: 16px 20px;
+}
+.pricing-dialog ::v-deep .el-dialog__title {
+  color: #fff;
+  font-weight: 500;
+}
+.pricing-dialog ::v-deep .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+.pricing-dialog ::v-deep .el-dialog__body {
+  padding: 20px;
+  background: #f5f7fa;
+}
+
+.pricing-container {
+  max-height: 60vh;
+  overflow-y: auto;
+}
+
+/* 头部提示 */
+.pricing-header {
+  background: linear-gradient(135deg, #ecf5ff 0%, #f0f9ff 100%);
+  border: 1px solid #d9ecff;
+  border-radius: 8px;
+  padding: 12px 16px;
+  margin-bottom: 16px;
+  display: flex;
+  align-items: center;
+  font-size: 13px;
+  color: #409EFF;
+}
+
+/* 卡片列表 */
+.pricing-list {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+/* 定价卡片 */
+.pricing-card {
+  background: #fff;
+  border-radius: 10px;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
+  border: 1px solid #ebeef5;
+  overflow: hidden;
+  transition: all 0.3s ease;
+}
+.pricing-card:hover {
+  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+  transform: translateY(-1px);
+}
+
+/* 卡片头部 */
+.pricing-card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  background: linear-gradient(135deg, #fafbfc 0%, #f5f7fa 100%);
+  border-bottom: 1px solid #ebeef5;
+}
+.module-title .el-tag {
+  font-size: 13px;
+  font-weight: 500;
+  padding: 4px 12px;
+}
+
+/* 卡片主体 */
+.pricing-card-body {
+  padding: 16px;
+  display: flex;
+  align-items: center;
+  gap: 20px;
+}
+
+/* 定价区域 */
+.global-pricing,
+.tenant-pricing {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+/* 分割线 */
+.divider {
+  width: 1px;
+  height: 60px;
+  background: linear-gradient(180deg, transparent 0%, #dcdfe6 50%, transparent 100%);
+}
+
+/* 价格项 */
+.price-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+.price-label {
+  font-size: 13px;
+  color: #606266;
+  white-space: nowrap;
+}
+.price-value {
+  font-size: 14px;
+  font-weight: 500;
+  font-family: 'Microsoft YaHei', sans-serif;
+}
+.price-value.global {
+  color: #909399;
+  background: #f4f4f5;
+  padding: 4px 12px;
+  border-radius: 4px;
+}
+
+/* 底部按钮 */
+.pricing-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+  padding: 16px 20px;
+  background: #fff;
+  border-top: 1px solid #ebeef5;
+}
+.pricing-footer .el-button {
+  padding: 10px 24px;
+  border-radius: 6px;
+}
 </style>