traffic-audit-web/src/views/UserManage.vue
@@ -1,2 +1,186 @@
<template><div>用户管理</div></template>
<script>export default { name: 'UserManage' }</script>
<template>
  <el-container>
    <el-header>交通统计报表审核系统 - 用户管理</el-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">企业解释</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-item index="/login" @click="logout">退出登录</el-menu-item>
        </el-menu>
      </el-aside>
      <el-main>
        <div class="page-scroll">
          <h3>用户管理(负责模块决定报表生成页默认显示哪些模块)</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'
const MODULE_OPTIONS = [
  { key: 'freight', label: '货运报表' },
  { key: 'passenger', label: '公路旅客报表' },
  { key: 'invest', label: '投资报表' },
  { key: 'energy', label: '能耗报表' },
  { key: 'cityPassenger', label: '城市客运报表' }
]
export default {
  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: {
    logout() {
      localStorage.removeItem('token'); localStorage.removeItem('username'); localStorage.removeItem('realName'); localStorage.removeItem('modules')
      this.$router.push('/login')
    },
    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>