lizhi 3 місяців тому
батько
коміт
9d9ec97ddb

+ 2 - 2
index.html

@@ -9,8 +9,8 @@
       content="逐趣CRM - A modern admin dashboard template built with Vue 3, TypeScript, and Element Plus."
     />
     <link rel="shortcut icon" type="image/x-icon" href="/assets/favicon-B33nFcrs.ico" />
-    <script type="module" crossorigin src="/assets/index-DF0Gm8tW.js"></script>
-    <link rel="modulepreload" crossorigin href="/assets/vendor-CgKklJ4r.js">
+    <script type="module" crossorigin src="/assets/index-CNubFkuS.js"></script>
+    <link rel="modulepreload" crossorigin href="/assets/vendor-qzIqKK2b.js">
     <link rel="stylesheet" crossorigin href="/assets/index-D7z-dQEN.css">
   </head>
 

+ 5 - 0
protected/controllers/CommonController.php

@@ -78,4 +78,9 @@ class CommonController extends Controller
     {
         echo (new DBTable(Helper::getGetString('t1')))->getTableHtml();
     }
+
+    public function actionInfo()
+    {
+        echo (new DBTable(Helper::getGetString('t1')))->getDetailHtml();
+    }
 }

+ 2 - 2
protected/controllers/SchoolController.php

@@ -22,7 +22,7 @@ class SchoolController extends Controller
 
     public function actionList()
     {
-        $filter = [];
+        $filter = ['is_del' => 0];
         $address = Helper::getArrParam($_POST, 'address', Helper::PARAM_KEY_TYPE['array_string']);
         $filter['province'] = $address[0]?? null;
         $filter['city'] = $address[1]?? null;
@@ -61,7 +61,7 @@ class SchoolController extends Controller
         if ($id < 1) {
             Helper::error('参数错误');
         }
-        DB::deleteById('school', $id);
+        Db::updateById('school', ['is_del' => 1], $id);
         Helper::ok();
     }
 

+ 41 - 0
protected/include/DBColumn.php

@@ -127,6 +127,47 @@ class DBColumn {
     }
 
     /**
+     * 获取字段的表单html和验证规则
+     * TODO: 这里可能要vue,react, layui, ... 代码
+     * @return array
+     */
+    public function getDetailHtml() :string
+    {
+        $html = "{{ info.{$this->field} }}";
+        $type = $this->getTsType();
+        $textType = 'string';
+        if ($type == 'string') {
+            // 图片处理
+            if (Helper::hasAnyString($this->field, ['img', 'image'])) {
+                // 单张图片
+            } elseif (Helper::hasAnyString($this->field, ['images', 'imgs'])) {
+                // 多张图片
+            } elseif (Helper::hasAnyString($this->field, ['_id'])) {
+                // select
+            } elseif (Helper::hasAnyString($this->field, ['ids'])) {
+                // 多选框
+            } else {
+                // input
+                $max = $this->getMaxLength();
+                if ($max > 64 || Helper::hasAnyString($this->field, ['text'])) {
+                    $textType = 'textarea';
+                }
+            }
+        } elseif ($type == '0|1') {
+            $data = $this->getCommentArr();
+            $html = "{{ info.{$this->field} ? '{$data[1]}' : '$data[0]'}}";
+        }
+        $elHtml = $textType != 'textarea' ? ':xs="24" :lg="8" :sm="12"' : ':span=24';
+        return <<<Detail
+
+        <el-col {$elHtml}> 
+             <label>{$this->comment}:</label> <span>{$html}</span>
+        </el-col> 
+
+Detail;
+    }
+
+    /**
      * 生成input
      * @param  string  $type
      * @param  int  $maxLength

+ 34 - 0
protected/include/DBTable.php

@@ -9,6 +9,7 @@ class DBTable {
     const array FORM_EXCLUDE_COLUMNS = ['create_date', 'update_date', 'is_delete', 'id', 'city', 'area', 'province'];
     const array INFO_EXCLUDE_COLUMNS = ['create_date', 'update_date', 'is_delete'];
     const array TABLE_EXCLUDE_COLUMNS = ['create_date', 'update_date', 'is_delete'];
+    const array DETAIL_EXCLUDE_COLUMNS = ['is_delete'];
     public function __construct(string $name) {
         $this->name = strtolower($name);
         $this->tableName = DB::formTableName($name);
@@ -70,6 +71,12 @@ class DBTable {
         echo "<p style='color: red;'>npx prettier --write $filePath</p>";
     }
 
+    public function echoDetail()
+    {
+        echo $this->getFormDefault();
+        list($template, $rule) = $this->getFormAndRule();
+    }
+
     /**
      * 生成ts接口
      * @return string
@@ -141,6 +148,33 @@ typescript;
         return [$content, $ruleHtml];
     }
 
+    /**
+     * 生成详情
+     * @return array
+     */
+    public function getDetailHtml(): string
+    {
+        echo $this->getFormDefault();
+        $html = '';
+        foreach ($this->columnArray as $column) {
+            if (in_array($column['Field'], self::DETAIL_EXCLUDE_COLUMNS)) {
+                continue;
+            }
+            $html.= (new DBColumn($column))->getDetailHtml();
+        }
+        $content =  <<<typescript
+<template>
+  <div>
+    <el-row :gutter="20" class="detail">
+    {$html}
+    </el-row>
+  </div>
+</template>
+typescript;
+        file_put_contents(PROJECT_PATH . "/protected/runtime/detail.vue", $content);
+        return $content;
+    }
+
 
     /**
      * 生成table字段配置

+ 1 - 1
web/src/api/schoolApi.ts

@@ -12,7 +12,7 @@ export class schoolApi {
 
   // 详情
   static info(id: number) {
-    return request.post<Api.School.SchoolListItem>({
+    return request.post<Api.School.SchoolInfo>({
       url: 'school/info',
       params: {id:id}
       // 自定义请求头

BIN
web/src/assets/icons/system/iconfont.json.gz


+ 1 - 0
web/src/components/core/forms/art-button-table/index.vue

@@ -64,6 +64,7 @@
     height: 34px;
     padding: 0 10px;
     margin-right: 10px;
+    margin-top: 5px;
     font-size: 13px;
     line-height: 34px;
     color: #666;

+ 12 - 1
web/src/router/routes/asyncRoutes.ts

@@ -107,7 +107,7 @@ export const asyncRoutes: AppRouteRecord[] = [
         component: RoutesAlias.SchoolList,
         meta: {
           title: 'menus.school.list',
-          keepAlive: true,
+          keepAlive: false,
           authList: [
             {
               id: 120101,
@@ -137,6 +137,17 @@ export const asyncRoutes: AppRouteRecord[] = [
           keepAlive: true,
           activePath: '/school/list' // 激活菜单路径
         }
+      },
+      {
+        path: 'info',
+        name: 'SchoolInfo',
+        component: RoutesAlias.SchoolInfo,
+        meta: {
+          title: 'menus.school.info',
+          isHide: true,
+          keepAlive: true,
+          activePath: '/school/list' // 激活菜单路径
+        }
       }
       // {
       //   id: 1202,

+ 1 - 1
web/src/router/routesAlias.ts

@@ -56,7 +56,7 @@ export enum RoutesAlias {
   ExamplesTablesTree = '/examples/tables/tree', // 左右布局表格示例
   ExamplesSearchBar = '/examples/forms/search-bar', // 搜索表单示例
   SchoolList = '/school/list', // 学校列表
-  SchoolDetail = '/school/detail', // 学校详情
+  SchoolInfo = '/school/info', // 学校详情
   SchoolEdit = '/school/edit', // 编辑学校
   SchoolRelation = '/school/relation' // 学校关系
 }

+ 25 - 0
web/src/typings/api.d.ts

@@ -131,6 +131,31 @@ declare namespace Api {
   }
 
   namespace School {
+
+    interface SchoolInfo {
+      id: number
+      name: string // 名称,
+      province: string // 省,
+      city: string // 市,
+      area: string // 区,
+      address: string // 详细地址,
+      person_num: string // 在校人数,
+      bind_user_id: number // 负责人,
+      is_eleme_in_school: 0 | 1 // 是否有饿了么校内站 0-无 1-有,
+      is_eleme_out_school: 0 | 1 // 是否有饿了么校外站 0-无 1-有,
+      is_meituan_in_school: 0 | 1 // 是否有美团校内站 0-无 1-有,
+      is_meituan_out_school: 0 | 1 // 是否有美团校外站 0-无 1-有,
+      can_go_upstairs: 0 | 1 // 是否能上楼 0-能 1-不能,
+      is_cooperate: 0 | 1 // 是否合作 0-无 1-有,
+      can_ride: 0 | 1 // 是否允许骑电动车 0-能 1-不能,
+      dormitory_distribution: string // 宿舍分布情况,
+      qucan_station_distribution: string // 校门口取餐点离宿舍情况,
+      out_business_description: string // 校外商圈情况,
+      memo: string // 备注,
+      create_date: string // 创建时间
+      update_date: string // 更新时间
+    }
+
     interface SchoolListData {
       records: SchoolListItem[]
       current: number

+ 0 - 269
web/src/views/school/comment/index.vue

@@ -1,269 +0,0 @@
-<template>
-  <div class="page-content">
-    <h1 class="title">留言墙</h1>
-    <p class="desc">每一份留言都记录了您的想法,也为我们提供了珍贵的回忆</p>
-
-    <div class="list">
-      <ul class="offset">
-        <li
-          class="comment-box"
-          v-for="item in commentList"
-          :key="item.id"
-          :style="{ background: randomColor() }"
-          @click="openDrawer(item)"
-        >
-          <p class="date">{{ item.date }}</p>
-          <p class="content">{{ item.content }}</p>
-          <div class="bottom">
-            <div class="left">
-              <span><i class="iconfont-sys">&#xe6eb;</i>{{ item.collection }}</span>
-              <span><i class="iconfont-sys">&#xe6e9;</i>{{ item.comment }}</span>
-            </div>
-            <div class="right">
-              <span>{{ item.userName }}</span>
-            </div>
-          </div>
-        </li>
-      </ul>
-    </div>
-
-    <ElDrawer
-      lDrawer
-      v-model="showDrawer"
-      :lock-scroll="false"
-      :size="360"
-      modal-class="comment-modal"
-    >
-      <template #header>
-        <h4>详情</h4>
-      </template>
-      <template #default>
-        <div class="drawer-default">
-          <div class="comment-box" :style="{ background: randomColor() }">
-            <p class="date">{{ clickItem.date }}</p>
-            <p class="content">{{ clickItem.content }}</p>
-            <div class="bottom">
-              <div class="left">
-                <span><i class="iconfont-sys">&#xe6eb;</i>{{ clickItem.collection }}</span>
-                <span><i class="iconfont-sys">&#xe6e9;</i>{{ clickItem.comment }}</span>
-              </div>
-              <div class="right">
-                <span>{{ clickItem.userName }}</span>
-              </div>
-            </div>
-          </div>
-
-          <!-- 评论组件 -->
-          <CommentWidget />
-        </div>
-      </template>
-      <template #footer>
-        <div>
-          <!-- <ElButton @click="cancelClick">cancel</ElButton> -->
-          <!-- <ElButton type="primary" @click="confirmClick">confirm</ElButton> -->
-        </div>
-      </template>
-    </ElDrawer>
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { commentList } from '@/mock/temp/commentList'
-  const showDrawer = ref(false)
-
-  defineOptions({ name: 'ArticleComment' })
-
-  // const colorList = reactive([
-  //   'rgba(216, 248, 255, 0.8)',
-  //   'rgba(253, 223, 217, 0.8)',
-  //   'rgba(252, 230, 240, 0.8)',
-  //   'rgba(211, 248, 240, 0.8)',
-  //   'rgba(255, 234, 188, 0.8)',
-  //   'rgba(245, 225, 255, 0.8)',
-  //   'rgba(225, 230, 254, 0.8)'
-  // ])
-
-  const colorList = reactive([
-    '#D8F8FF',
-    '#FDDFD9',
-    '#FCE6F0',
-    '#D3F8F0',
-    '#FFEABC',
-    '#F5E1FF',
-    '#E1E6FE'
-  ])
-
-  let lastColor: string | null = null
-
-  const randomColor = () => {
-    let newColor: string
-
-    do {
-      const index = Math.floor(Math.random() * colorList.length)
-      newColor = colorList[index]
-    } while (newColor === lastColor)
-
-    lastColor = newColor
-    return newColor
-  }
-
-  const clickItem = ref({
-    id: 1,
-    date: '2024-9-3',
-    content: '加油!学好Node 自己写个小Demo',
-    collection: 5,
-    comment: 8,
-    userName: '匿名'
-  })
-
-  const openDrawer = (item: any) => {
-    showDrawer.value = true
-    clickItem.value = item
-  }
-</script>
-
-<style lang="scss" scoped>
-  .page-content {
-    background-color: transparent !important;
-    box-shadow: none !important;
-
-    :deep(.comment-modal) {
-      background-color: transparent;
-    }
-
-    .title {
-      margin-top: 20px;
-      font-size: 36px;
-      font-weight: 500;
-    }
-
-    .desc {
-      margin-top: 15px;
-      font-size: 14px;
-      color: var(--art-text-gray-600);
-    }
-
-    .list {
-      margin-top: 40px;
-
-      .offset {
-        display: flex;
-        flex-wrap: wrap;
-        width: calc(100% + 16px);
-      }
-    }
-
-    .comment-box {
-      position: relative;
-      box-sizing: border-box;
-      width: calc(20% - 16px);
-      aspect-ratio: 16 / 12;
-      padding: 16px;
-      margin: 0 16px 16px 0;
-      cursor: pointer;
-      background-color: #eae2cb;
-      transition: all 0.3s;
-
-      &:hover {
-        transform: translateY(-5px);
-      }
-
-      .date {
-        font-size: 12px;
-        color: #949494;
-      }
-
-      .content {
-        margin-top: 16px;
-        font-size: 14px;
-        color: #333;
-      }
-
-      .bottom {
-        position: absolute;
-        bottom: 16px;
-        left: 0;
-        box-sizing: border-box;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        width: 100%;
-        padding: 0 16px;
-
-        .left {
-          display: flex;
-          align-items: center;
-
-          span {
-            display: flex;
-            align-items: center;
-            margin-right: 20px;
-            font-size: 12px;
-            color: #949494;
-
-            i {
-              margin-right: 5px;
-            }
-          }
-        }
-
-        .right {
-          span {
-            font-size: 14px;
-            color: #333;
-          }
-        }
-      }
-    }
-
-    .drawer-default {
-      .comment-box {
-        width: 100%;
-
-        &:hover {
-          transform: translateY(0);
-        }
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-notebook) {
-    .page-content {
-      .comment-box {
-        width: calc(25% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-ipad-pro) {
-    .page-content {
-      .comment-box {
-        width: calc(33.333% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-ipad) {
-    .page-content {
-      .comment-box {
-        width: calc(50% - 16px);
-      }
-    }
-  }
-
-  @media screen and (max-width: $device-phone) {
-    .page-content {
-      .comment-box {
-        width: calc(100% - 16px);
-      }
-    }
-  }
-
-  .dark {
-    .page-content {
-      .comment-box {
-        color: #333 !important;
-      }
-    }
-  }
-</style>

+ 0 - 116
web/src/views/school/detail/index.vue

@@ -1,116 +0,0 @@
-<template>
-  <div class="article-detail page-content">
-    <div class="content">
-      <h1>{{ articleTitle }}</h1>
-      <div class="markdown-body" v-highlight v-html="articleHtml"></div>
-    </div>
-    <ArtBackToTop />
-  </div>
-</template>
-
-<script setup lang="ts">
-  import '@/assets/styles/markdown.scss'
-  import '@/assets/styles/one-dark-pro.scss'
-  import { useCommon } from '@/composables/useCommon'
-  import axios from 'axios'
-  // import 'highlight.js/styles/atom-one-dark.css';
-  // import 'highlight.js/styles/vs2015.css';
-
-  defineOptions({ name: 'ArticleDetail' })
-
-  const articleId = ref(0)
-  const router = useRoute()
-  const articleTitle = ref('')
-  const articleHtml = ref('')
-
-  onMounted(() => {
-    useCommon().scrollToTop()
-    articleId.value = Number(router.query.id)
-    getArticleDetail()
-  })
-
-  const getArticleDetail = async () => {
-    if (articleId.value) {
-      const res = await axios.get('https://www.qiniu.lingchen.kim/blog_detail.json')
-      if (res.data.code === 200) {
-        articleTitle.value = res.data.data.title
-        articleHtml.value = res.data.data.html_content
-      }
-    }
-  }
-</script>
-
-<style lang="scss">
-  .article-detail {
-    .content {
-      max-width: 800px;
-      margin: auto;
-      margin-top: 60px;
-
-      .markdown-body {
-        margin-top: 60px;
-
-        img {
-          width: 100%;
-          border: 1px solid var(--art-gray-200);
-        }
-
-        pre {
-          position: relative;
-
-          &:hover {
-            .copy-button {
-              opacity: 1;
-            }
-          }
-
-          &::before {
-            position: absolute;
-            top: 0;
-            left: 50px;
-            width: 1px;
-            height: 100%;
-            content: '';
-            background: #0a0a0e;
-          }
-        }
-
-        .code-wrapper {
-          overflow-x: auto;
-        }
-
-        .line-number {
-          position: sticky;
-          left: 0;
-          z-index: 2;
-          box-sizing: border-box;
-          display: inline-block;
-          width: 50px;
-          margin-right: 10px;
-          font-size: 14px;
-          color: #9e9e9e;
-          text-align: center;
-        }
-
-        .copy-button {
-          position: absolute;
-          top: 6px;
-          right: 6px;
-          z-index: 1;
-          width: 40px;
-          height: 40px;
-          font-size: 20px;
-          line-height: 40px;
-          color: #999;
-          text-align: center;
-          cursor: pointer;
-          background-color: #000;
-          border: none;
-          border-radius: 8px;
-          opacity: 0;
-          transition: all 0.2s;
-        }
-      }
-    }
-  }
-</style>

+ 123 - 0
web/src/views/school/info.vue

@@ -0,0 +1,123 @@
+<template>
+  <div>
+    <el-row :gutter="20" class="detail">
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>名称:</label> <span>{{ info.name }}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>地区:</label> <span>{{ [info.province, info.city, info.area].join(' / ') }}</span>
+      </el-col>
+
+      <el-col :span=24>
+        <label>详细地址:</label> <span>{{ info.address }}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>在校人数:</label> <span>{{ info.person_num }}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>负责人:</label> <span>{{ info.bind_user_id }}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否有饿了么校内站:</label> <span>{{ info.is_eleme_in_school ? '有' : '无'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否有饿了么校外站:</label> <span>{{ info.is_eleme_out_school ? '有' : '无'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否有美团校内站:</label> <span>{{ info.is_meituan_in_school ? '有' : '无'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否有美团校外站:</label> <span>{{ info.is_meituan_out_school ? '有' : '无'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否能上楼:</label> <span>{{ info.can_go_upstairs ? '不能' : '能'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否合作:</label> <span>{{ info.is_cooperate ? '已合作' : '未合作'}}</span>
+      </el-col>
+
+      <el-col :xs="24" :lg="8" :sm="12">
+        <label>是否允许骑电动车:</label> <span>{{ info.can_ride ? '不能' : '能'}}</span>
+      </el-col>
+
+      <el-col :span=24>
+        <label>宿舍分布情况:</label> <span>{{ info.dormitory_distribution }}</span>
+      </el-col>
+
+      <el-col :span=24>
+        <label>校门口取餐点离宿舍情况:</label> <span>{{ info.qucan_station_distribution }}</span>
+      </el-col>
+
+      <el-col :span=24>
+        <label>校外商圈情况:</label> <span>{{ info.out_business_description }}</span>
+      </el-col>
+
+      <el-col :span=24>
+        <label>备注:</label> <span>{{ info.memo }}</span>
+      </el-col>
+
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+// 初始化表单数据
+import {schoolApi} from "@/api/schoolApi";
+import {onMounted} from "vue";
+
+const DefaultData = <Api.School.SchoolInfo>{
+  id:0,
+  province: '',
+  city: '',
+  area: '',
+  name: '',
+  distinct:[],
+  address: '',
+  person_num: '',
+  bind_user_id: 0,
+  is_eleme_in_school: 0,
+  is_eleme_out_school: 0,
+  is_meituan_in_school: 0,
+  is_meituan_out_school: 0,
+  can_go_upstairs: 0,
+  is_cooperate: 0,
+  can_ride: 0,
+  dormitory_distribution: '',
+  qucan_station_distribution: '',
+  out_business_description: '',
+  memo: '',
+  create_date:'',
+  update_date:'',
+}
+const info = reactive<Api.School.SchoolInfo>({ ...DefaultData })
+onMounted(() => {
+  schoolApi.info(parseInt(useRoute().query.id as string)).then((res) => {
+    Object.assign(info, res)
+    console.log(`%c info == `, 'background:#41b883 ; padding:1px; color:#fff', info);
+  })
+})
+</script>
+
+<style scoped>
+.detail {
+  padding-top: 20px;
+  font-size: 14px;
+  .el-col {
+    margin-bottom: 30px;
+    label {
+      font-weight: bold;
+      margin-right: 10px;
+    }
+  }
+}
+</style>

+ 354 - 267
web/src/views/school/list/index.vue

@@ -17,42 +17,41 @@
 
       <!-- 表格 -->
       <ArtTable
-        :loading="loading"
-        :data="data"
-        :columns="columns"
-        :pagination="pagination"
-        @selection-change="handleSelectionChange"
-        @pagination:size-change="handleSizeChange"
-        @pagination:current-change="handleCurrentChange"
+          :loading="loading"
+          :data="data"
+          :columns="columns"
+          :pagination="pagination"
+          @pagination:size-change="handleSizeChange"
+          @pagination:current-change="handleCurrentChange"
       >
         <template #person_num="scope">
           <ElInput
-            v-model="scope.row.person_num"
-            placeholder="scope.row.person_num"
-            @blur="doUpdateAttr(scope)"
+              v-model="scope.row.person_num"
+              placeholder="scope.row.person_num"
+              @blur="doUpdateAttr(scope)"
           />
         </template>
 
         <template #is_eleme_in_school="scope">
           <ElSwitch
-            v-model="scope.row.is_eleme_in_school"
-            @change="doUpdateAttr(scope)"
-            :active-value="1"
-            :inactive-value="0"
-            :before-change="() => isMounted"
+              v-model="scope.row.is_eleme_in_school"
+              @change="doUpdateAttr(scope)"
+              :active-value="1"
+              :inactive-value="0"
+              :before-change="() => isMounted"
           />
         </template>
 
         <template #is_eleme_out_school="scope">
-          <ElSwitch v-model="scope.row.is_eleme_out_school" @change="doUpdateAttr(scope)" />
+          <ElSwitch v-model="scope.row.is_eleme_out_school" @change="doUpdateAttr(scope)"/>
         </template>
 
         <template #is_meituan_in_school="scope">
-          <ElSwitch v-model="scope.row.is_meituan_in_school" @change="doUpdateAttr(scope)" />
+          <ElSwitch v-model="scope.row.is_meituan_in_school" @change="doUpdateAttr(scope)"/>
         </template>
 
         <template #is_meituan_out_school="scope">
-          <ElSwitch v-model="scope.row.is_meituan_out_school" @change="doUpdateAttr(scope)" />
+          <ElSwitch v-model="scope.row.is_meituan_out_school" @change="doUpdateAttr(scope)"/>
         </template>
       </ArtTable>
 
@@ -65,294 +64,382 @@
       <!--        @submit="handleDialogSubmit"-->
       <!--      />-->
     </ElCard>
+
+    <el-drawer
+        v-model="drawer"
+        :title="currentRow.name"
+        direction="rtl"
+        size="60%"
+    >
+      <ElRow>
+        <ElCol :sm="12">
+          <ElRow class="detail">
+
+            <el-col :sm="12">
+              <label>在校人数:</label> <span>{{ currentRow.person_num }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>地区:</label> <span>{{
+                [currentRow.province, currentRow.city, currentRow.area].join(' / ')
+              }}</span>
+            </el-col>
+
+            <el-col :span=24>
+              <label>详细地址:</label> <span>{{ currentRow.address }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>负责人:</label> <span>{{ currentRow.bind_user_id }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否有饿了么校内站:</label> <span>{{ currentRow.is_eleme_in_school ? '有' : '无' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否有饿了么校外站:</label> <span>{{ currentRow.is_eleme_out_school ? '有' : '无' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否有美团校内站:</label> <span>{{ currentRow.is_meituan_in_school ? '有' : '无' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否有美团校外站:</label> <span>{{ currentRow.is_meituan_out_school ? '有' : '无' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否能上楼:</label> <span>{{ currentRow.can_go_upstairs ? '不能' : '能' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否合作:</label> <span>{{ currentRow.is_cooperate ? '已合作' : '未合作' }}</span>
+            </el-col>
+
+            <el-col :sm="12">
+              <label>是否允许骑电动车:</label> <span>{{ currentRow.can_ride ? '不能' : '能' }}</span>
+            </el-col>
+
+            <el-col :span=24>
+              <label>宿舍分布情况:</label> <span>{{ currentRow.dormitory_distribution }}</span>
+            </el-col>
+
+            <el-col :span=24>
+              <label>校门口取餐点离宿舍情况:</label> <span>{{ currentRow.qucan_station_distribution }}</span>
+            </el-col>
+
+            <el-col :span=24>
+              <label>校外商圈情况:</label> <span>{{ currentRow.out_business_description }}</span>
+            </el-col>
+
+            <el-col :span=24>
+              <label>备注:</label> <span>{{ currentRow.memo }}</span>
+            </el-col>
+          </ElRow>
+        </ElCol>
+        <ElCol :sm="12">
+          <ElImage src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"/>
+        </ElCol>
+      </ElRow>
+    </el-drawer>
   </div>
 </template>
 
 <script setup lang="ts">
-  import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
-  import { ACCOUNT_TABLE_DATA } from '@/mock/temp/formData'
-  import { ElMessageBox, ElMessage, ElTag, ElImage, ElButton, ElInput } from 'element-plus'
-  import { useTable } from '@/composables/useTable'
-  import { schoolApi } from '@/api/schoolApi'
-  import UserSearch from './user-search.vue'
-  import { useUserStore } from '@/store/modules/user'
-  import { useWorktabStore } from '@/store/modules/worktab'
-  import SchoolListItem = Api.School.SchoolListItem
-  import { render } from 'vue'
-  import EmojiText from '@utils/ui/emojo'
-  import { router } from '@/router'
-  import { RoutesAlias } from '@/router/routesAlias'
-
-  defineOptions({ name: 'User' })
-
-  const worktabStore = useWorktabStore()
-
-  type UserListItem = Api.User.UserListItem
-  const { list } = schoolApi
-
-  // 弹窗相关
-  const dialogType = ref<Form.DialogType>('add')
-  const dialogVisible = ref(false)
-  const currentUserData = ref<Partial<UserListItem>>({})
-
-  // 选中行
-  const selectedRows = ref<UserListItem[]>([])
-
-  // 搜索表单
-  const searchForm = ref({
-    name: '',
-    is_cooperate: -1,
-    address: []
-  })
+import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
+import {ElMessageBox, ElMessage, ElTag, ElImage, ElButton, ElInput} from 'element-plus'
+import {useTable} from '@/composables/useTable'
+import {schoolApi} from '@/api/schoolApi'
+import UserSearch from './user-search.vue'
+import {useUserStore} from '@/store/modules/user'
+import EmojiText from '@utils/ui/emojo'
+import {router} from '@/router'
+import {RoutesAlias} from '@/router/routesAlias'
+
+defineOptions({name: 'User'})
+
+const {list} = schoolApi
+const drawer = ref(false)
+
+// 搜索表单
+const searchForm = ref({
+  name: '',
+  is_cooperate: -1,
+  address: []
+})
 
-  const {
-    columns,
-    columnChecks,
-    data,
-    loading,
-    pagination,
-    getData,
-    searchParams,
-    resetSearchParams,
-    handleSizeChange,
-    handleCurrentChange,
-    refreshData
-  } = useTable<SchoolListItem>({
-    // 核心配置
-    core: {
-      apiFn: list,
-      apiParams: {
-        current: 1,
-        size: 20,
-        ...searchForm.value
+const currentRow = ref<Partial<Api.School.SchoolListItem>>({})
+
+const {
+  columns,
+  columnChecks,
+  data,
+  loading,
+  pagination,
+  getData,
+  searchParams,
+  resetSearchParams,
+  handleSizeChange,
+  handleCurrentChange,
+  refreshData
+} = useTable<Api.School.SchoolListItem>({
+  // 核心配置
+  core: {
+    apiFn: list,
+    apiParams: {
+      current: 1,
+      size: 20,
+      ...searchForm.value
+    },
+    // 排除 apiParams 中的属性
+    excludeParams: [],
+    columnsFactory: () => [
+      {
+        prop: 'name', label: '学校(校区)', formatter: (row) => {
+          return h(ElButton, {
+            type: 'primary',
+            link: true,
+            onClick: () => showDrawer(row),
+            style: {"text-decoration": 'underline'}
+          }, () => row.name)
+        }
+      },
+      {prop: 'area', label: '地区', formatter: (row) => row.province + row.city + row.area},
+      {prop: 'address', label: '详细地址'},
+      {
+        prop: 'canteen',
+        label: '关联食堂',
+        // sortable: true,
+        // checked: false, // 隐藏列
+        formatter: (row) => {
+          return h(ElButton, {type: 'primary'}, () => '查看')
+        }
+      },
+      {
+        prop: 'concat',
+        label: '关系人',
+        formatter: (row) => {
+          return h(ElButton, {type: 'primary'}, () => '查看')
+        }
       },
-      // 排除 apiParams 中的属性
-      excludeParams: [],
-      columnsFactory: () => [
-        { prop: 'name', label: '学校(校区)' },
-        { prop: 'area', label: '地区', formatter: (row) => row.province + row.city + row.area },
-        { prop: 'address', label: '详细地址' },
-        {
-          prop: 'canteen',
-          label: '关联食堂',
-          // sortable: true,
-          // checked: false, // 隐藏列
-          formatter: (row) => {
-            return h(ElButton, { type: 'primary' }, () => '查看')
-          }
-        },
-        {
-          prop: 'concat',
-          label: '关系人',
-          formatter: (row) => {
-            return h(ElButton, { type: 'primary' }, () => '查看')
-          }
-        },
-        {
-          prop: 'is_cooperate',
-          label: '合作状态',
-          formatter: (row) => {
-            return h(ElTag, { type: row.is_cooperate ? 'success' : 'danger' }, () =>
+      {
+        prop: 'is_cooperate',
+        label: '合作状态',
+        formatter: (row) => {
+          return h(ElTag, {type: row.is_cooperate ? 'success' : 'danger'}, () =>
               row.is_cooperate ? '已合作' : '未合作'
-            )
-          }
-        },
-        { prop: 'person_num', label: '在校人数', useSlot: true },
-        { prop: 'is_eleme_in_school', label: '是否有饿了么校内站', useSlot: true },
-        {
-          prop: 'is_eleme_out_school',
-          label: '是否有饿了么校外站',
-          formatter: (row) => {
-            return h(ElTag, { type: row.is_eleme_out_school ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {prop: 'person_num', label: '在校人数', useSlot: true},
+      {prop: 'is_eleme_in_school', label: '是否有饿了么校内站', useSlot: true},
+      {
+        prop: 'is_eleme_out_school',
+        label: '是否有饿了么校外站',
+        formatter: (row) => {
+          return h(ElTag, {type: row.is_eleme_out_school ? 'success' : 'danger'}, () =>
               row.is_eleme_out_school ? '有' : '无'
-            )
-          }
-        },
-        {
-          prop: 'is_meituan_in_school',
-          label: '是否有美团校内站',
-          formatter: (row) => {
-            return h(ElTag, { type: row.is_meituan_in_school ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'is_meituan_in_school',
+        label: '是否有美团校内站',
+        formatter: (row) => {
+          return h(ElTag, {type: row.is_meituan_in_school ? 'success' : 'danger'}, () =>
               row.is_meituan_in_school ? '有' : '无'
-            )
-          }
-        },
-        {
-          prop: 'is_meituan_out_school',
-          label: '是否有美团校外站',
-          formatter: (row) => {
-            return h(ElTag, { type: row.is_meituan_out_school ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'is_meituan_out_school',
+        label: '是否有美团校外站',
+        formatter: (row) => {
+          return h(ElTag, {type: row.is_meituan_out_school ? 'success' : 'danger'}, () =>
               row.is_meituan_out_school ? '有' : '无'
-            )
-          }
-        },
-        {
-          prop: 'can_go_upstairs',
-          label: '是否能上楼',
-          formatter: (row) => {
-            return h(ElTag, { type: row.can_go_upstairs ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'can_go_upstairs',
+        label: '是否能上楼',
+        formatter: (row) => {
+          return h(ElTag, {type: row.can_go_upstairs ? 'success' : 'danger'}, () =>
               row.can_go_upstairs ? '不能' : '能'
-            )
-          }
-        },
-        {
-          prop: 'can_ride',
-          label: '是否允许骑电动车',
-          formatter: (row) => {
-            return h(ElTag, { type: row.can_ride ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'can_ride',
+        label: '是否允许骑电动车',
+        formatter: (row) => {
+          return h(ElTag, {type: row.can_ride ? 'success' : 'danger'}, () =>
               row.can_ride ? '不能' : '能'
-            )
-          }
-        },
-        {
-          prop: 'dormitory_distribution',
-          label: '宿舍分布情况',
-          formatter: (row) => {
-            return h(ElTag, { type: row.dormitory_distribution ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'dormitory_distribution',
+        label: '宿舍分布情况',
+        formatter: (row) => {
+          return h(ElTag, {type: row.dormitory_distribution ? 'success' : 'danger'}, () =>
               row.dormitory_distribution ? '是' : '否'
-            )
-          }
-        },
-        {
-          prop: 'qucan_station_distribution',
-          label: '校门口取餐点离宿舍情况',
-          formatter: (row) => {
-            return h(ElTag, { type: row.qucan_station_distribution ? 'success' : 'danger' }, () =>
+          )
+        }
+      },
+      {
+        prop: 'qucan_station_distribution',
+        label: '校门口取餐点离宿舍情况',
+        formatter: (row) => {
+          return h(ElTag, {type: row.qucan_station_distribution ? 'success' : 'danger'}, () =>
               row.qucan_station_distribution ? '是' : '否'
-            )
-          }
-        },
-        { prop: 'out_business_description', label: '校外商圈情况', showOverflowTooltip: true },
-        { prop: 'memo', label: '备注', showOverflowTooltip: true },
-        {
-          prop: 'operation',
-          label: '操作',
-          width: 120,
-          fixed: 'right', // 固定列
-          formatter: (row) =>
+          )
+        }
+      },
+      {prop: 'out_business_description', label: '校外商圈情况', showOverflowTooltip: true},
+      {prop: 'memo', label: '备注', showOverflowTooltip: true},
+      {
+        prop: 'operation',
+        label: '操作',
+        width: 120,
+        fixed: 'right', // 固定列
+        formatter: (row) =>
             useUserStore().checkAuth(110202) &&
             h('div', [
               h(ArtButtonTable, {
+                type: 'view',
+                onClick: () => view(row.id)
+              }),
+              h(ArtButtonTable, {
                 type: 'edit',
                 onClick: () => edit(row.id)
               }),
               useUserStore().checkAuth(110203) &&
-                h(ArtButtonTable, {
-                  type: 'delete',
-                  onClick: () => deleteUser(row.id)
-                })
+              h(ArtButtonTable, {
+                type: 'delete',
+                onClick: () => deleteUser(row.id)
+              })
             ])
-        }
-      ]
-    }
-  })
+      }
+    ]
+  }
+})
 
-  // TODO:不知道为什么初始化会触发switch的change事件
-  const isMounted = ref(false)
-  onMounted(() => {
-    setTimeout(() => {
-      isMounted.value = true
-    }, 1000)
-  })
+// TODO:不知道为什么初始化会触发switch的change事件
+const isMounted = ref(false)
+onMounted(() => {
+  setTimeout(() => {
+    isMounted.value = true
+  }, 1000)
+})
 
-  const doUpdateAttr = (scope: any) => {
-    console.log(`%c scope == `, 'background:#41b883 ; padding:1px; color:#fff', scope)
-    if (!scope.row.id || !isMounted.value) {
-      return
-    }
-    schoolApi
-      .updateAttr({ id: scope.row.id, attr: scope.prop, value: scope.row[scope.prop] })
+const doUpdateAttr = (scope: any) => {
+  console.log(`%c scope == `, 'background:#41b883 ; padding:1px; color:#fff', scope)
+  if (!scope.row.id || !isMounted.value) {
+    return
+  }
+  schoolApi
+      .updateAttr({id: scope.row.id, attr: scope.prop, value: scope.row[scope.prop]})
       .then(() => {
         ElMessage.success(`${EmojiText[200]} 修改成功`)
       })
-  }
+}
 
-  /**
-   * 搜索处理
-   * @param params 参数
-   */
-  const handleSearch = (params: Record<string, any>) => {
-    Object.assign(searchParams, { ...params })
-    getData()
-  }
+/**
+ * 搜索处理
+ * @param params 参数
+ */
+const handleSearch = (params: Record<string, any>) => {
+  Object.assign(searchParams, {...params})
+  getData()
+}
 
-  /**
-   * 编辑
-   */
-  const edit = (id?: number): void => {
-    // if (worktabStore.getTab(RoutesAlias.SchoolEdit)) {
-    //   console.log(`%c RoutesAlias.SchoolEdit == `, 'background:#41b883 ; padding:1px; color:#fff', RoutesAlias.SchoolEdit);
-    //   worktabStore.removeTab(RoutesAlias.SchoolEdit)
-    // }
-    router.push({
-      path: RoutesAlias.SchoolEdit,
-      query: {
-        id: id
-      }
-    })
-  }
+/**
+ * 编辑
+ */
+const edit = (id?: number): void => {
+  // if (worktabStore.getTab(RoutesAlias.SchoolEdit)) {
+  //   console.log(`%c RoutesAlias.SchoolEdit == `, 'background:#41b883 ; padding:1px; color:#fff', RoutesAlias.SchoolEdit);
+  //   worktabStore.removeTab(RoutesAlias.SchoolEdit)
+  // }
+  router.push({
+    path: RoutesAlias.SchoolEdit,
+    query: {
+      id: id
+    }
+  })
+}
 
-  /**
-   * 删除用户
-   */
-  const deleteUser = (id: number): void => {
-    ElMessageBox.confirm(`确定要删除该学校吗?`, '删除学校', {
-      confirmButtonText: '确定',
-      cancelButtonText: '取消',
-      type: 'error'
-    }).then(() => {
-      schoolApi.delete({ id: id }).then(() => {
-        ElMessage.success(`${EmojiText[200]} 删除成功`)
-        setTimeout(() => {
-          getData()
-        }, 1000)
-      })
-    })
-  }
+/**
+ * 查看
+ */
+const view = (id: number): void => {
+  router.push({
+    path: RoutesAlias.SchoolInfo,
+    query: {
+      id: id
+    }
+  })
+}
+
+const showDrawer = (row: Api.School.SchoolListItem): void => {
+  drawer.value = true;
+  currentRow.value = row
+}
 
-  /**
-   * 处理弹窗提交事件
-   */
-  const handleDialogSubmit = async () => {
-    try {
-      dialogVisible.value = false
-      currentUserData.value = {}
-      // 延迟更新 不然数据可能没更新
+/**
+ * 删除
+ */
+const deleteUser = (id: number): void => {
+  ElMessageBox.confirm(`确定要删除该学校吗?`, '删除学校', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'error'
+  }).then(() => {
+    schoolApi.delete({id: id}).then(() => {
+      ElMessage.success(`${EmojiText[200]} 删除成功`)
       setTimeout(() => {
-        refreshData()
+        getData()
       }, 1000)
-    } catch (error) {
-      console.error('提交失败:', error)
-    }
-  }
-
-  /**
-   * 处理表格行选择变化
-   */
-  const handleSelectionChange = (selection: UserListItem[]): void => {
-    selectedRows.value = selection
-    console.log('选中行数据:', selectedRows.value)
-  }
+    })
+  })
+}
 </script>
 
 <style lang="scss" scoped>
-  .user-page {
-    :deep(.user) {
-      .avatar {
-        width: 40px;
-        height: 40px;
-        margin-left: 0;
-        border-radius: 6px;
-      }
+.user-page {
+  :deep(.user) {
+    .avatar {
+      width: 40px;
+      height: 40px;
+      margin-left: 0;
+      border-radius: 6px;
+    }
 
-      > div {
-        margin-left: 10px;
+    > div {
+      margin-left: 10px;
 
-        .user-name {
-          font-weight: 500;
-          color: var(--art-text-gray-800);
-        }
+      .user-name {
+        font-weight: 500;
+        color: var(--art-text-gray-800);
       }
     }
   }
+}
+.el-drawer__title {
+  font-size: 20px;
+  font-weight: bold;
+}
+.detail {
+  //padding-top: 20px;
+  font-size: 14px;
+  .el-col {
+    margin-bottom: 30px;
+    label {
+      font-weight: bold;
+      margin-right: 5px;
+    }
+  }
+}
 </style>

+ 0 - 359
web/src/views/school/publish/index.vue

@@ -1,359 +0,0 @@
-<template>
-  <div class="article-edit">
-    <div>
-      <div class="editor-wrap">
-        <!-- 文章标题、类型 -->
-        <ElRow :gutter="10">
-          <ElCol :span="18">
-            <ElInput
-              v-model.trim="articleName"
-              placeholder="请输入文章标题(最多100个字符)"
-              maxlength="100"
-            />
-          </ElCol>
-          <ElCol :span="6">
-            <ElSelect v-model="articleType" placeholder="请选择文章类型" filterable>
-              <ElOption
-                v-for="item in articleTypes"
-                :key="item.id"
-                :label="item.name"
-                :value="item.id"
-              />
-            </ElSelect>
-          </ElCol>
-        </ElRow>
-
-        <!-- 富文本编辑器 -->
-        <ArtWangEditor class="el-top" v-model="editorHtml" />
-
-        <div class="form-wrap">
-          <h2>发布设置</h2>
-          <!-- 图片上传 -->
-          <ElForm>
-            <ElFormItem label="封面">
-              <div class="el-top upload-container">
-                <ElUpload
-                  class="cover-uploader"
-                  :action="uploadImageUrl"
-                  :headers="uploadHeaders"
-                  :show-file-list="false"
-                  :on-success="onSuccess"
-                  :on-error="onError"
-                  :before-upload="beforeUpload"
-                >
-                  <div v-if="!cover" class="upload-placeholder">
-                    <ElIcon class="upload-icon"><Plus /></ElIcon>
-                    <div class="upload-text">点击上传封面</div>
-                  </div>
-                  <img v-else :src="cover" class="cover-image" />
-                </ElUpload>
-                <div class="el-upload__tip">建议尺寸 16:9,jpg/png 格式</div>
-              </div>
-            </ElFormItem>
-            <ElFormItem label="可见">
-              <ElSwitch v-model="visible" />
-            </ElFormItem>
-          </ElForm>
-
-          <div style="display: flex; justify-content: flex-end">
-            <ElButton type="primary" @click="submit" style="width: 100px">
-              {{ pageMode === PageModeEnum.Edit ? '保存' : '发布' }}
-            </ElButton>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- <div class="outline-wrap">
-        <div class="item" v-for="(item, index) in outlineList" :key="index">
-          <p :class="`level${item.level}`">{{ item.text }}</p>
-        </div>
-      </div> -->
-  </div>
-</template>
-
-<script setup lang="ts">
-  import { Plus } from '@element-plus/icons-vue'
-  import { ApiStatus } from '@/utils/http/status'
-  import { ElMessage } from 'element-plus'
-  import { useUserStore } from '@/store/modules/user'
-  import EmojiText from '@/utils/ui/emojo'
-  import { PageModeEnum } from '@/enums/formEnum'
-  import axios from 'axios'
-  import { useCommon } from '@/composables/useCommon'
-
-  defineOptions({ name: 'ArticlePublish' })
-
-  const route = useRoute()
-
-  const userStore = useUserStore()
-  let { accessToken } = userStore
-
-  // 上传路径
-  const uploadImageUrl = `${import.meta.env.VITE_API_URL}/api/common/upload`
-  // 传递 token
-  const uploadHeaders = { Authorization: accessToken }
-
-  let pageMode: PageModeEnum = PageModeEnum.Add // 页面类型 新增 | 编辑
-  const articleName = ref('') // 文章标题
-  const articleType = ref() // 文章类型
-  const articleTypes = ref() // 类型列表
-  const editorHtml = ref('') // 编辑器内容
-  const createDate = ref('') // 创建时间
-  const cover = ref('') // 图片
-  const visible = ref(true) // 可见
-  // const outlineList = ref()
-
-  onMounted(() => {
-    useCommon().scrollToTop()
-    getArticleTypes()
-    initPageMode()
-  })
-
-  // 初始化页面类型 新增 | 编辑
-  const initPageMode = () => {
-    const { id } = route.query
-    pageMode = id ? PageModeEnum.Edit : PageModeEnum.Add
-    if (pageMode === PageModeEnum.Edit && id) {
-      initEditArticle()
-    } else {
-      initAddArticle()
-    }
-  }
-
-  // 初始化编辑文章的逻辑
-  const initEditArticle = () => {
-    getArticleDetail()
-  }
-
-  // 初始化新增文章逻辑
-  const initAddArticle = () => {
-    createDate.value = formDate(useNow().value)
-  }
-
-  // 获取文章类型
-  const getArticleTypes = async () => {
-    try {
-      const response = await axios.get('https://www.qiniu.lingchen.kim/classify.json')
-      if (response.data.code === 200) {
-        articleTypes.value = response.data.data
-      }
-    } catch (error) {
-      console.error('Error fetching JSON data:', error)
-    }
-    // try {
-    //   const res = await ArticleService.getArticleTypes({})
-    //   if (res.code === ApiStatus.success) {
-    //     articleTypes.value = res.data
-    //   }
-    // } catch (err) { }
-  }
-
-  const getArticleDetail = async () => {
-    const res = await axios.get('https://www.qiniu.lingchen.kim/blog_list.json')
-
-    if (res.data.code === ApiStatus.success) {
-      let { title, blog_class, html_content } = res.data.data
-      articleName.value = title
-      articleType.value = Number(blog_class)
-      editorHtml.value = html_content
-    }
-  }
-
-  // const getOutline = (content: string) => {
-  //   const regex = /<h([1-3])>(.*?)<\/h\1>/g
-  //   const headings = []
-  //   let match
-
-  //   while ((match = regex.exec(content)) !== null) {
-  //     headings.push({ level: match[1], text: match[2] })
-  //   }
-  //   outlineList.value = headings
-  // }
-
-  // 提交
-  const submit = () => {
-    if (pageMode === PageModeEnum.Edit) {
-      editArticle()
-    } else {
-      addArticle()
-    }
-  }
-
-  // 格式化日期
-  const formDate = (date: string | Date): string => {
-    return useDateFormat(date, 'YYYY-MM-DD').value
-  }
-
-  // 验证输入
-  const validateArticle = () => {
-    if (!articleName.value) {
-      ElMessage.error(`请输入文章标题`)
-      return false
-    }
-
-    if (!articleType.value) {
-      ElMessage.error(`请选择文章类型`)
-      return false
-    }
-
-    if (editorHtml.value === '<p><br></p>') {
-      ElMessage.error(`请输入文章内容`)
-      return false
-    }
-
-    if (!cover.value) {
-      ElMessage.error(`请上传图片`)
-      return false
-    }
-
-    return true
-  }
-
-  // 添加文章
-  const addArticle = async () => {
-    try {
-      if (!validateArticle()) return
-      editorHtml.value = delCodeTrim(editorHtml.value)
-    } catch (err) {
-      console.error(err)
-    }
-  }
-
-  // 编辑文章
-  const editArticle = async () => {
-    try {
-      if (!validateArticle()) return
-
-      editorHtml.value = delCodeTrim(editorHtml.value)
-    } catch (err) {
-      console.error(err)
-    }
-  }
-
-  const delCodeTrim = (content: string): string => {
-    return content.replace(/(\s*)<\/code>/g, '</code>')
-  }
-
-  const onSuccess = (response: any) => {
-    cover.value = response.data.url
-    ElMessage.success(`图片上传成功 ${EmojiText[200]}`)
-  }
-
-  const onError = () => {
-    ElMessage.error(`图片上传失败 ${EmojiText[500]}`)
-  }
-
-  // 添加上传前的校验
-  const beforeUpload = (file: File) => {
-    const isImage = file.type.startsWith('image/')
-    const isLt2M = file.size / 1024 / 1024 < 2
-
-    if (!isImage) {
-      ElMessage.error('只能上传图片文件!')
-      return false
-    }
-    if (!isLt2M) {
-      ElMessage.error('图片大小不能超过 2MB!')
-      return false
-    }
-    return true
-  }
-</script>
-
-<style lang="scss" scoped>
-  .article-edit {
-    .editor-wrap {
-      max-width: 1000px;
-      margin: 20px auto;
-
-      .el-top {
-        margin-top: 10px;
-      }
-
-      .form-wrap {
-        padding: 20px;
-        margin-top: 20px;
-        background-color: var(--art-main-bg-color);
-        border: 1px solid var(--art-border-color);
-        border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
-
-        h2 {
-          margin-bottom: 20px;
-          font-size: 20px;
-          font-weight: 500;
-        }
-      }
-    }
-
-    .outline-wrap {
-      box-sizing: border-box;
-      width: 280px;
-      padding: 20px;
-      border: 1px solid #e3e3e3;
-      border-radius: 8px;
-
-      .item {
-        p {
-          height: 30px;
-          font-size: 13px;
-          line-height: 30px;
-          cursor: pointer;
-        }
-
-        .level3 {
-          padding-left: 10px;
-        }
-      }
-    }
-
-    .upload-container {
-      .cover-uploader {
-        position: relative;
-        overflow: hidden;
-        cursor: pointer;
-        border-radius: 6px;
-        transition: var(--el-transition-duration);
-
-        &:hover {
-          border-color: var(--el-color-primary);
-        }
-
-        .upload-placeholder {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          justify-content: center;
-          width: 260px;
-          height: 160px;
-          border: 1px dashed #d9d9d9;
-          border-radius: 6px;
-
-          .upload-icon {
-            font-size: 28px;
-            color: #8c939d;
-          }
-
-          .upload-text {
-            margin-top: 8px;
-            font-size: 14px;
-            color: #8c939d;
-          }
-        }
-
-        .cover-image {
-          display: block;
-          width: 260px;
-          height: 160px;
-          object-fit: cover;
-        }
-      }
-
-      .el-upload__tip {
-        margin-top: 8px;
-        font-size: 12px;
-        color: #666;
-      }
-    }
-  }
-</style>

+ 0 - 2
web/src/views/system/user/modules/user-dialog.vue

@@ -37,9 +37,7 @@
 </template>
 
 <script setup lang="ts">
-  import { ROLE_LIST_DATA } from '@/mock/temp/formData'
   import type { FormInstance, FormRules } from 'element-plus'
-  import { ElMessage } from 'element-plus'
   import { UserService } from '@/api/usersApi'
 
   interface Props {