From d42a044f6f7ca289a7b37affa00b6c385cd13b62 Mon Sep 17 00:00:00 2001
From: xyc <jc_xiong@hotmail.com>
Date: 星期一, 31 八月 2026 09:46:26 +0800
Subject: [PATCH] fix: 报表生成页 el-tabs 同样触发布局无限循环风险,改为 el-radio-group 切换(与 /import 修复一致),并清理 DataImport 残留 el-tabs 样式

---
 traffic-audit-web/src/views/UserManage.vue |  185 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 183 insertions(+), 2 deletions(-)

diff --git a/traffic-audit-web/src/views/UserManage.vue b/traffic-audit-web/src/views/UserManage.vue
index 6deae10..93694d5 100644
--- a/traffic-audit-web/src/views/UserManage.vue
+++ b/traffic-audit-web/src/views/UserManage.vue
@@ -1,2 +1,183 @@
-锘�<template><div>鐢ㄦ埛绠$悊</div></template>
-<script>export default { name: 'UserManage' }</script>
+<template>
+  <el-container direction="vertical">
+    <app-header title="浜ら�氱粺璁℃姤琛ㄥ鏍哥郴缁� - 鐢ㄦ埛绠$悊"></app-header>
+    <el-container>
+      <el-aside width="200px">
+        <el-menu router :default-active="activeMenu">
+          <el-menu-item index="/dashboard">宸ヤ綔鍙�</el-menu-item>
+          <el-menu-item index="/import">鏁版嵁瀵煎叆</el-menu-item>
+          <el-menu-item index="/data">鏁版嵁鏌ョ湅</el-menu-item>
+          <el-menu-item index="/explain">浼佷笟瑙i噴</el-menu-item>
+          <el-menu-item index="/audit">瀹℃牳缁撴灉</el-menu-item>
+          <el-menu-item index="/report">鎶ヨ〃鐢熸垚</el-menu-item>
+          <el-menu-item index="/rules">瑙勫垯绠$悊</el-menu-item>
+          <el-menu-item index="/users">鐢ㄦ埛绠$悊</el-menu-item>
+        </el-menu>
+      </el-aside>
+      <el-main>
+        <div class="page-scroll">
+          <h3>鐢ㄦ埛绠$悊锛堣礋璐fā鍧楀喅瀹氭姤琛ㄧ敓鎴愰〉榛樿鏄剧ず鍝簺妯″潡锛�</h3>
+          <div class="toolbar">
+            <el-input v-model="keyword" placeholder="鐢ㄦ埛鍚� / 濮撳悕" clearable style="width:220px" @keyup.enter.native="loadList" @clear="loadList"></el-input>
+            <el-button type="primary" icon="el-icon-search" @click="loadList">鏌ヨ</el-button>
+            <el-button type="success" icon="el-icon-plus" @click="openEdit()">鏂板鐢ㄦ埛</el-button>
+          </div>
+          <div class="table-area">
+            <el-table :data="list" border stripe size="small" v-loading="loading">
+              <el-table-column prop="id" label="ID" width="60" align="center"></el-table-column>
+              <el-table-column prop="username" label="鐢ㄦ埛鍚�" width="120"></el-table-column>
+              <el-table-column prop="realName" label="濮撳悕" width="100"></el-table-column>
+              <el-table-column prop="phone" label="鎵嬫満鍙�" width="130"></el-table-column>
+              <el-table-column label="璐熻矗妯″潡" min-width="240">
+                <template slot-scope="{ row }">
+                  <el-tag v-for="mk in moduleKeys(row.modules)" :key="mk" size="mini" style="margin-right:4px">{{ moduleLabel(mk) }}</el-tag>
+                  <span v-if="!row.modules" style="color:#999">鏈厤缃紙榛樿鍏ㄩ儴鍙锛�</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="鐘舵��" width="80" align="center">
+                <template slot-scope="{ row }">
+                  <el-tag :type="row.status === 1 ? 'success' : 'info'" size="mini">{{ row.status === 1 ? '鍚敤' : '鍋滅敤' }}</el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column prop="createdAt" label="鍒涘缓鏃堕棿" width="170"></el-table-column>
+              <el-table-column label="鎿嶄綔" width="140" align="center">
+                <template slot-scope="{ row }">
+                  <el-button type="text" size="mini" @click="openEdit(row)">缂栬緫</el-button>
+                  <el-button type="text" size="mini" style="color:#f56c6c" @click="removeUser(row)">鍒犻櫎</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </div>
+      </el-main>
+    </el-container>
+
+    <el-dialog :title="form.id ? '缂栬緫鐢ㄦ埛' : '鏂板鐢ㄦ埛'" :visible.sync="dialogVisible" width="520px" @closed="resetForm">
+      <el-form ref="userForm" :model="form" :rules="formRules" label-width="90px">
+        <el-form-item label="鐢ㄦ埛鍚�" prop="username">
+          <el-input v-model="form.username" :disabled="!!form.id" placeholder="鐧诲綍璐﹀彿"></el-input>
+        </el-form-item>
+        <el-form-item label="瀵嗙爜" :prop="form.id ? '' : 'password'">
+          <el-input v-model="form.password" type="password" :placeholder="form.id ? '鐣欑┖鍒欎笉淇敼瀵嗙爜' : '璇疯緭鍏ュ垵濮嬪瘑鐮�'"></el-input>
+        </el-form-item>
+        <el-form-item label="濮撳悕" prop="realName">
+          <el-input v-model="form.realName" placeholder="鐪熷疄濮撳悕"></el-input>
+        </el-form-item>
+        <el-form-item label="鎵嬫満鍙�">
+          <el-input v-model="form.phone" placeholder="閫夊~"></el-input>
+        </el-form-item>
+        <el-form-item label="璐熻矗妯″潡">
+          <el-checkbox-group v-model="form.modules">
+            <el-checkbox v-for="mo in moduleOptions" :key="mo.key" :label="mo.key">{{ mo.label }}</el-checkbox>
+          </el-checkbox-group>
+          <div class="form-tip">鎶ヨ〃鐢熸垚椤甸粯璁ゅ彧鏄剧ず鍕鹃�夌殑妯″潡锛涗笉鍕惧垯鍏ㄩ儴鍙</div>
+        </el-form-item>
+        <el-form-item label="鐘舵��">
+          <el-radio-group v-model="form.status">
+            <el-radio :label="1">鍚敤</el-radio>
+            <el-radio :label="0">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" :loading="saving" @click="saveUser">淇濆瓨</el-button>
+      </div>
+    </el-dialog>
+  </el-container>
+</template>
+<script>
+import api from '@/api'
+import AppHeader from '@/components/AppHeader.vue'
+const MODULE_OPTIONS = [
+  { key: 'freight', label: '璐ц繍鎶ヨ〃' },
+  { key: 'passenger', label: '鍏矾鏃呭鎶ヨ〃' },
+  { key: 'invest', label: '鎶曡祫鎶ヨ〃' },
+  { key: 'energy', label: '鑳借�楁姤琛�' },
+  { key: 'cityPassenger', label: '鍩庡競瀹㈣繍鎶ヨ〃' }
+]
+export default {
+  components: { AppHeader },
+  name: 'UserManage',
+  data() {
+    return {
+      activeMenu: '/users',
+      keyword: '',
+      list: [],
+      loading: false,
+      dialogVisible: false,
+      saving: false,
+      moduleOptions: MODULE_OPTIONS,
+      form: { id: null, username: '', password: '', realName: '', phone: '', modules: [], status: 1 },
+      formRules: {
+        username: [{ required: true, message: '璇疯緭鍏ョ敤鎴峰悕', trigger: 'blur' }],
+        password: [{ required: true, message: '璇疯緭鍏ュ垵濮嬪瘑鐮�', trigger: 'blur' }]
+      }
+    }
+  },
+  mounted() { this.loadList() },
+  methods: {
+    loadList() {
+      this.loading = true
+      api.get('/user/list', { params: { keyword: this.keyword } })
+        .then(res => { this.list = res.data || [] })
+        .catch(e => { if (e && e._toast) return; this.$message.error('鐢ㄦ埛鍒楄〃鍔犺浇澶辫触') })
+        .finally(() => { this.loading = false })
+    },
+    moduleKeys(modules) {
+      if (!modules) return []
+      return modules.split(',').filter(Boolean)
+    },
+    moduleLabel(key) {
+      const mo = MODULE_OPTIONS.find(x => x.key === key)
+      return mo ? mo.label : key
+    },
+    openEdit(row) {
+      this.dialogVisible = true
+      this.$nextTick(() => {
+        if (row && row.id) {
+          this.form = {
+            id: row.id, username: row.username, password: '', realName: row.realName || '',
+            phone: row.phone || '', modules: this.moduleKeys(row.modules), status: row.status === 0 ? 0 : 1
+          }
+        } else {
+          this.form = { id: null, username: '', password: '', realName: '', phone: '', modules: [], status: 1 }
+        }
+        this.$refs.userForm && this.$refs.userForm.clearValidate()
+      })
+    },
+    resetForm() {
+      this.form = { id: null, username: '', password: '', realName: '', phone: '', modules: [], status: 1 }
+      this.$refs.userForm && this.$refs.userForm.clearValidate()
+    },
+    saveUser() {
+      this.$refs.userForm.validate(valid => {
+        if (!valid) return
+        this.saving = true
+        api.post('/user/save', this.form)
+          .then(() => {
+            this.$message.success('淇濆瓨鎴愬姛')
+            this.dialogVisible = false
+            this.loadList()
+          })
+          .catch(err => {
+            if (err && err._toast) return
+            const d = err && err.response && err.response.data
+            this.$message.error((d && d.message) || '淇濆瓨澶辫触')
+          })
+          .finally(() => { this.saving = false })
+      })
+    },
+    removeUser(row) {
+      this.$confirm('纭畾鍒犻櫎鐢ㄦ埛銆�' + (row.realName || row.username) + '銆嶅悧锛�', '鍒犻櫎纭', { type: 'warning' })
+        .then(() => api.post('/user/delete', null, { params: { id: row.id } }))
+        .then(() => { this.$message.success('宸插垹闄�'); this.loadList() })
+        .catch(() => {})
+    }
+  }
+}
+</script>
+<style scoped>
+.toolbar { margin-bottom: 12px; display: flex; gap: 8px; align-items: center; }
+.form-tip { font-size: 12px; color: #909399; line-height: 1.6; margin-top: 4px; }
+</style>

--
Gitblit v1.9.1