bzkf3 vor 2 Jahren
Ursprung
Commit
13e50a04d1

+ 0 - 129
src/components/archives-card/index.vue

@@ -1,129 +0,0 @@
-<script setup lang="ts">
-import { type_archives_item } from '~/types';
-
-const props = defineProps<{
-  d: type_archives_item;
-  disabled?: boolean;
-  storage?: boolean;
-}>()
-let Storagekey = `a_${new Date().toLocaleDateString()}_${props.d.dxz_id}`
-const archivesStorage = sessionStorage.getItem(Storagekey)
-const form = reactive<type_archives_item>(
-  (props.storage && archivesStorage)
-    ? JSON.parse(archivesStorage)
-    : Object.assign(
-      {
-        user_id: '',
-        dxxd_date: '',
-        dxxd_lfzxm: '',
-        dxxd_school_name: '',
-        dxxd_class_name: '',
-        dxxd_jfls: '',
-        dxxd_address: '',
-        dxxd_phone: '',
-        dxxd_zxyy: '',
-        dxxd_lfzzs: '',
-        dxxd_clfs: '',
-        dxxd_wxdj: '',
-        dxxd_cbfx: '',
-      },
-      props.d
-    )
-)
-
-defineExpose({
-  form
-})
-
-watch(
-  form,
-  (val) => {
-    props.storage && sessionStorage.setItem(Storagekey, JSON.stringify(val))
-  },
-  {
-    deep: true,
-    immediate: true
-  })
-
-watch(
-  () => props.d.dxz_id,
-  (val, oldVal) => {
-    if (oldVal !== val) {
-      Storagekey = `a_${new Date().toLocaleDateString()}_${val}`
-      form.dxz_id = val
-    }
-  },
-  {
-    deep: true,
-    // immediate: true
-  })
-</script>
-
-<template>
-  <div class="border border-dark-100 divide-y divide-dark-100 w-full text-xs">
-
-    <div class="flex justify-between items-end">
-      <div class="p-1 text-base font-bold">咨询记录</div>
-      <!-- #ifdef DEV -->
-      <!-- {{ d.dxz_id }} -->
-      <!-- #endif -->
-      <div class="p-1">{{ form.dxxd_date }}</div>
-    </div>
-    <div class="divide-x divide-dark-100 flex justify-start">
-      <div class="flex-none px-2px w-66px leading-7">来访者姓名</div>
-      <div class="px-2px flex-auto leading-7 max-w-56px"><el-input autocomplete="off" name="dxxd_lfzxm"
-          :disabled="disabled" input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_lfzxm"
-          size="small"></el-input>
-      </div>
-      <div class="flex-none px-2px w-30px leading-7">学校</div>
-      <div class="px-2px flex-auto leading-7"><el-input autocomplete="off" name="dxxd_school_name" :disabled="disabled"
-          input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_school_name" size="small"></el-input></div>
-      <div class="flex-none px-2px w-30px leading-7">班级</div>
-      <div class="px-2px flex-auto leading-7"><el-input autocomplete="off" name="dxxd_class_name" :disabled="disabled"
-          input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_class_name" size="small"></el-input></div>
-    </div>
-    <div class="divide-x divide-dark-100 flex justify-start">
-      <div class="flex-none px-2px w-66px leading-7">接访教师</div>
-      <div class="px-2px flex-auto leading-7 max-w-56px"><el-input autocomplete="off" name="dxxd_jfls"
-          :disabled="disabled" input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_jfls"
-          size="small"></el-input>
-      </div>
-      <div class="flex-none px-2px w-30px leading-7">住址</div>
-      <div class="px-2px flex-auto leading-7"><el-input autocomplete="off" name="dxxd_address" :disabled="disabled"
-          input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_address" size="small"></el-input></div>
-      <div class="flex-none px-2px w-30px leading-7">电话</div>
-      <div class="px-2px flex-auto leading-7"><el-input autocomplete="off" name="dxxd_phone" :disabled="disabled"
-          input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_phone" size="small"></el-input></div>
-    </div>
-
-    <div class="p-1">
-      <div>咨询缘由: </div><el-input autocomplete="off" name="dxxd_zxyy" :disabled="disabled"
-        input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_zxyy" type="textarea" resize="none"
-        :rows="2"></el-input>
-    </div>
-    <div class="p-1">
-      <div>来访者自述:</div><el-input autocomplete="off" name="dxxd_lfzzs" :disabled="disabled"
-        input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_lfzzs" type="textarea" resize="none"
-        :rows="2"></el-input>
-    </div>
-    <div class="p-1">
-      <div>初步分析:</div><el-input autocomplete="off" name="dxxd_cbfx" :disabled="disabled"
-        input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_cbfx" type="textarea" resize="none"
-        :rows="2"></el-input>
-    </div>
-    <div class="p-1">
-      <div>危机等级:</div>
-      <el-radio-group v-model="form.dxxd_wxdj" size="small" :disabled="disabled">
-        <el-radio label="1">一般</el-radio>
-        <el-radio label="2">严重</el-radio>
-        <el-radio label="3">重大</el-radio>
-      </el-radio-group>
-    </div>
-    <div class="p-1">
-      <div>处理方式:</div><el-input autocomplete="off" name="dxxd_clfs" :disabled="disabled"
-        input-style="font-size:12px;line-height:14px;" v-model="form.dxxd_clfs" type="textarea" resize="none"
-        :rows="2"></el-input>
-    </div>
-
-  </div>
-</template>

+ 0 - 34
src/components/chat-dialog/index.vue

@@ -1,34 +0,0 @@
-<script setup lang="ts">
-import { UseDraggable } from '@vueuse/components'
-
-let isOpen = $ref<boolean>(false)
-
-function handleClose(){
-    isOpen = false
-}
-defineExpose({
-  open() {
-    isOpen = true
-  },
-  close() {
-    handleClose()
-  }
-})
-</script>
-
-<template>
-  <UseDraggable v-if="isOpen" storage-key="chat-audio" storage-type="session" :initial-value="{ x: 584, y: 207 }"
-    class="fixed w-375px h-670px bg-hex-191919 cursor-move z-4000">
-    <div class="w-full flex justify-end items-center p-2 text-light-50 h-36px">
-      <i:clarity:window-min-line class="cursor-pointer" />
-      <i:ic:outline-close class="cursor-pointer" @click="handleClose" />
-    </div>
-    <div class="h-634px">
-      <slot></slot>
-    </div>
-  </UseDraggable>
-</template>
-
-<style scoped>
-
-</style>

+ 0 - 47
src/components/chat-stu-card/index.vue

@@ -1,47 +0,0 @@
-<script setup lang="ts">
-import type { type_dyaw_xlfw_zxhd } from '~/types';
-import { formatTimeToShow } from '~/utils/time'
-const props = defineProps<{ d: type_dyaw_xlfw_zxhd }>()
-
-const showMsg = $computed(() => {
-  switch (props.d.dxz_last_msg_type) {
-    case '1': return decodeURIComponent(props.d.dxz_last_msg);
-    case '2': return '[ 图片 ]';
-    case '3': return decodeURIComponent(props.d.dxz_last_msg);
-    case '4': return decodeURIComponent(props.d.dxz_last_msg);
-  }
-})
-</script>
-
-<template>
-  <div class="h-full flex w-full items-center space-x-4 cursor-pointer">
-    <el-avatar :size="48" :src="d.dxx_user_avatar"></el-avatar>
-    <div class="flex flex-col justify-evenly flex-auto">
-      <div class="flex justify-between">
-        <div class="font-medium">{{ props.d.dxz_stu_user_realname }}</div>
-        <div class="opacity-40 text-sm">{{ formatTimeToShow(d.dxz_last_msg_datetime) }}</div>
-      </div>
-      <div class="flex justify-between h-20px">
-        <template v-if="d.dxz_last_msg_datetime">
-          <div class="opacity-40 text-sm text_wrapper max-w-50 h-20px overflow-hidden" v-html="showMsg"></div>
-          <el-tag type="danger" effect="dark" round size="small" v-show="Number(d.dxz_unread_msg_num) > 0">{{
-            Number(d.dxz_unread_msg_num) > 99 ? '99+' : d.dxz_unread_msg_num
-          }}</el-tag>
-        </template>
-
-      </div>
-    </div>
-  </div>
-</template>
-
-<style scoped lang="scss">
-.text_wrapper :deep(p) {
-  margin: 0;
-  padding: 0;
-  text-overflow: ellipsis;
-  height: 100%;
-  white-space: nowrap;
-  overflow: hidden;
-  width: 100%;
-}
-</style>

+ 40 - 0
src/components/cuoti-detail/index.vue

@@ -0,0 +1,40 @@
+<script setup lang='ts'>
+const props = defineProps<{
+  d: any
+}>()
+
+
+function formatTime(s: string) {
+  return (new Date(s * 1000)).toLocaleString()
+}
+// TODO: 临时处理
+function translateString(str: string) {
+  return str.split('\n').map((item) => `<p>${item}</p>`).join('')
+}
+
+function formatFileString(s: string) {
+  return s.split(';').map((item) => window.GLOBAL_CONFIG.oss + item)
+}
+</script>
+
+<template>
+  <div class="space-y-4 mt-8">
+    <div class="flex justify-between items-end">
+      <div class="text-xl whitespace-nowrap overflow-ellipsis overflow-hidden w-max-420px">{{ d.dc_title }}</div>
+      <el-tag>{{ d.dc_keyword }}</el-tag>
+    </div>
+    <div class="flex space-x-6 text-gray-500">
+      <div>{{ d.dc_grade_name }}</div>
+      <div>{{ d.dc_subject_name }}</div>
+      <div>{{ d.create_user_realname }}</div>
+      <div>{{ (d.create_dateline) }}</div>
+    </div>
+
+    <div class=" text-gray-600 pt-4" v-html="translateString(d.dc_content)"></div>
+
+    <div class="mt-6 flex flex-wrap items-start space-y-4 space-x-4">
+      <el-image fit="contain" class="w-1/6" v-for="(src, idx) in formatFileString(d.dc_files)" :src="src"
+        :preview-src-list="formatFileString(d.dc_files)" :initial-index="idx" />
+    </div>
+  </div>
+</template>

+ 0 - 61
src/components/info-item/index.vue

@@ -1,61 +0,0 @@
-<script setup lang="ts">
-import type { type_dyaw_xlfw_zxhd_log } from '~/types';
-import { formatTimestamp, formatTimeToShow } from '~/utils/time';
-const props = defineProps<{
-  left: boolean,
-  virtualName?: string,
-  w: number,
-  d: type_dyaw_xlfw_zxhd_log
-}>()
-
-
-const id2name = {
-  [props.d.dxzl_stu_user_id]: props.virtualName ?? props.d.dxzl_stu_user_realname,
-  [props.d.dxzl_tea_user_id]: props.d.dxzl_tea_user_realname,
-}
-
-
-const showMsg = $computed(() => decodeURIComponent(props.d.dxzl_last_msg_content!))
-</script>
-
-<template>
-  <div class="w-full" :class="left ? 'left' : 'right'">
-    <div class="flex items-end r_flex-row-reverse gap-4">
-      <div>{{ id2name[d.create_user_id] }}</div>
-      <div class="text-sm text-hex-AFB2B6">{{ formatTimeToShow(parseInt(props.d.create_dateline) * 1000) }}</div>
-    </div>
-    <div class="flex mt-2 r_flex-row-reverse">
-      <div class=" py-10px px-25px rounded-3xl min-h-44px bg-white text_wrapper" :style="`max-width:${props.w}px`"
-        v-html="showMsg">
-      </div>
-    </div>
-
-  </div>
-</template>
-
-<style scoped lang="scss">
-.text_wrapper :deep(p) {
-  margin: 0;
-  padding: 0;
-  word-wrap: break-word;
-}
-
-
-.left {
-  .text_wrapper {
-    border-radius: 2px 24px 24px 24px;
-  }
-}
-
-.right {
-  .r_flex-row-reverse {
-    flex-direction: row-reverse;
-  }
-
-  .text_wrapper {
-    background-color: #F9A7BE;
-    color: white;
-    border-radius: 24px 2px 24px 24px;
-  }
-}
-</style>

+ 0 - 524
src/components/rtc-dialog/index.vue

@@ -1,524 +0,0 @@
-<script setup lang="ts">
-import AgoraRTC from "agora-rtc-sdk-ng"
-import type { IAgoraRTCClient, IMicrophoneAudioTrack, ICameraVideoTrack } from "agora-rtc-sdk-ng"
-import { CHAT_STATUS, CHAT_OPERATION } from '~/types';
-import { UseDraggable } from '@vueuse/components'
-import request from '~/utils/request';
-import type { type_dyaw_xlfw_zxhd, type_dyaw_xlfw_zxhd_log } from '~/types';
-import user, { UserRole } from '~/store/user';
-import { socketSend } from '~/utils/ws';
-import { formatOffsetSec } from '~/utils/time';
-import { ElMessage } from "element-plus";
-let offsetTimer: NodeJS.Timeout
-let offsetTime = $ref<number>(0)
-
-const emits = defineEmits<{
-  (event: 'update-info', info: Partial<type_dyaw_xlfw_zxhd_log>, isUpdate?: boolean): void;
-}>()
-
-let reqDate: Partial<type_dyaw_xlfw_zxhd_log>
-
-let dyaw_xlfw_zxhd = $ref<type_dyaw_xlfw_zxhd>()
-let mode = $ref<'audio' | 'video'>()
-let dxzl_id = $ref<string>()
-
-const otherInfo = $computed(() => {
-  if (UserRole === 'student')
-    return { id: dyaw_xlfw_zxhd?.dxz_tea_user_id, realname: dyaw_xlfw_zxhd?.dxz_tea_user_realname, avatar: dyaw_xlfw_zxhd?.dxx_tea_avatar }
-  if (UserRole === 'teacher')
-    return { id: dyaw_xlfw_zxhd?.dxz_stu_user_id, realname: dyaw_xlfw_zxhd?.dxz_stu_user_realname, avatar: dyaw_xlfw_zxhd?.dxx_user_avatar }
-})
-
-let isOpen = $ref<boolean>(false)
-let currentChatStatus = $ref<CHAT_STATUS>(CHAT_STATUS.WAITING_OTHERS_ACCEPT)
-
-let ws2: WebSocket;
-function handleClose() {
-  isOpen = false
-}
-defineExpose({
-  init(ws: WebSocket) {
-    ws2 = ws
-  },
-  open(d: type_dyaw_xlfw_zxhd, _mode: 'audio' | 'video') {
-    if (isOpen) return;
-    // isOpen = true
-    dyaw_xlfw_zxhd = d
-    reqDate = {
-      dxz_id: dyaw_xlfw_zxhd.dxz_id,
-      dxzl_stu_user_id: dyaw_xlfw_zxhd.dxz_stu_user_id,
-      dxzl_stu_user_realname: dyaw_xlfw_zxhd.dxz_stu_user_realname,
-      dxzl_tea_user_id: dyaw_xlfw_zxhd.dxz_tea_user_id,
-      dxzl_tea_user_realname: dyaw_xlfw_zxhd.dxz_tea_user_realname,
-    }
-
-    mode = _mode
-    handleAudioChatStart()
-  },
-  close() {
-    handleClose()
-  },
-  async publisher(
-    content:
-      // type_dyaw_xlfw_zxhd_log &
-      {
-        operate: CHAT_OPERATION
-        // rtcOptions?: TRtcOptions
-        mode?: 'audio' | 'video'
-        dyaw_xlfw_zxhd?: type_dyaw_xlfw_zxhd
-        fullSendData?: type_dyaw_xlfw_zxhd_log
-        dxzl_id?: string
-      }
-  ) {
-    console.log('publisher: ', content);
-    //
-    const { operate } = content
-    switch (operate) {
-      case CHAT_OPERATION.START:
-        currentChatStatus = CHAT_STATUS.WAITING_YOU_ACCEPT
-        // rtcOptions = content.rtcOptions!;
-        mode = content.mode!;
-        dyaw_xlfw_zxhd = content.dyaw_xlfw_zxhd!
-        dxzl_id = content.dxzl_id!
-        reqDate = {
-          dxz_id: dyaw_xlfw_zxhd.dxz_id,
-          dxzl_stu_user_id: dyaw_xlfw_zxhd.dxz_stu_user_id,
-          dxzl_stu_user_realname: dyaw_xlfw_zxhd.dxz_stu_user_realname,
-          dxzl_tea_user_id: dyaw_xlfw_zxhd.dxz_tea_user_id,
-          dxzl_tea_user_realname: dyaw_xlfw_zxhd.dxz_tea_user_realname,
-        }
-        isOpen = true
-        emits('update-info', content.fullSendData!)
-
-        break;
-      case CHAT_OPERATION.CANCEL:
-        emits('update-info', content.fullSendData!, true)
-        isOpen = false
-        break;
-      case CHAT_OPERATION.ACCEPT:
-        await rtcInstance.client!.publish(rtcInstance.localAudioTrack!);
-        if (mode === 'video')
-          await rtcInstance.client!.publish(rtcInstance.localVideoTrack!);
-        currentChatStatus = CHAT_STATUS.CHATING
-        offsetTime = 0
-        offsetTimer = setInterval(() => {
-          offsetTime = (offsetTime as number + 1)
-        }, 1000)
-        break;
-      case CHAT_OPERATION.DENY:
-        emits('update-info', content.fullSendData!, true)
-        isOpen = false
-        await rtcInstance.client?.leave();
-        break;
-      case CHAT_OPERATION.END:
-        emits('update-info', content.fullSendData!, true)
-        offsetTimer && clearInterval(offsetTimer)
-        isOpen = false
-        await rtcInstance.client!.leave();
-        handleSwitchUse(false)
-        break;
-      default:
-        break;
-    }
-  }
-})
-
-function handleInfoAdd(tip: string) {
-  const _reqDate = Object.assign({
-    dxzl_status: '1',
-    dxzl_type: mode === 'audio' ? '3' : '4',
-    dxzl_last_msg_content: encodeURIComponent(`【${mode === 'audio' ? '语音通话' : '视频通话'}】 ${tip}`),
-  }, reqDate)
-  return request({
-    url: '/dyaw/xlfw_zxhd_log/add',
-    data: {
-      dyaw_xlfw_zxhd_log: _reqDate
-    }
-  }).then(res => {
-    if (res.code === '1') {
-      const fullSendData = {
-        create_user_id: user.user_id,
-        create_dateline: Date.now().toString().slice(0, 10),
-        ..._reqDate,
-        dxzl_id: `${res.data.insert_id}`
-      } as type_dyaw_xlfw_zxhd_log
-      emits('update-info', fullSendData)
-      dxzl_id = `${res.data.insert_id}`
-      return fullSendData
-    }
-    return Promise.reject()
-  })
-}
-
-function handleInfoEdit(data: Partial<type_dyaw_xlfw_zxhd_log>, tip?: string) {
-  const _reqDate = Object.assign({
-    dxzl_last_msg_content: tip ? encodeURIComponent(`【${mode === 'audio' ? '语音通话' : '视频通话'}】 ${tip}`) : undefined,
-  }, data)
-  return request({
-    url: '/dyaw/xlfw_zxhd_log/edit',
-    data: {
-      dxzl_id: dxzl_id,
-      dyaw_xlfw_zxhd_log: _reqDate
-    }
-  }).then(res => {
-    if (res.code === '1') {
-      emits('update-info', {
-        dxzl_id: dxzl_id!,
-        ..._reqDate
-      }, true)
-      return {
-        dxzl_id: dxzl_id!,
-        ..._reqDate
-      }
-    }
-    return Promise.reject()
-  })
-}
-
-const LocalPlayerContainerRef = $ref<HTMLElement>()
-const RemotePlayerContainerRef = $ref<HTMLElement>()
-// ==========
-// chat audio/video
-// ==========
-let rtcInstance: {
-  client?: IAgoraRTCClient;
-  localAudioTrack?: IMicrophoneAudioTrack;
-  localVideoTrack?: ICameraVideoTrack
-} = {
-  client: undefined,
-  localAudioTrack: undefined,
-  localVideoTrack: undefined,
-}
-type TRtcOptions = {
-  appId: string;
-  channel: string;
-  token: string;
-}
-let rtcOptions: TRtcOptions;
-
-function handleSwitchUse(v: boolean) {
-  try {
-    audioInUse = v
-    videoInUse = v
-    rtcInstance?.localAudioTrack?.setEnabled(v)
-    rtcInstance?.localVideoTrack?.setEnabled(v)
-  } catch (error) {
-    console.error(error);
-  }
-}
-
-function initRtcClient() {
-  if (rtcInstance.client) {
-    handleSwitchUse(true)
-    return
-  };
-  const client = rtcInstance.client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
-
-  client.on("user-published", async (user, mediaType) => {
-    await client.subscribe(user, mediaType);
-    if (mediaType === "audio") {
-      const audioTrack = user.audioTrack;
-      audioTrack?.play();
-    } else {
-      const videoTrack = user.videoTrack;
-      videoTrack?.play(RemotePlayerContainerRef as HTMLElement);
-    }
-  });
-}
-
-async function getRtcOption() {
-  try {
-    await request({
-      url: '/dyaw/xlfw_zxhd/get_rtc_token',
-      data: {
-        dxz_id: dyaw_xlfw_zxhd?.dxz_id
-      }
-    }).then(async res => {
-      if (res.code === '1') {
-        let resp: { jgim_roomid: string; rtc_appid: string; rtc_token: string } = res.data.one_info
-        return rtcOptions = {
-          appId: resp.rtc_appid,
-          // channel: resp.jgim_roomid,
-          channel: dyaw_xlfw_zxhd!.dxz_id,
-          token: resp.rtc_token,
-          // uid: user.user_id
-        }
-      }
-    })
-  } catch (error) {
-    console.error(error);
-  }
-}
-
-async function handleAudioChatStart() {
-  try {
-    isOpen = true
-    currentChatStatus = CHAT_STATUS.WAITING_OTHERS_ACCEPT
-    let isBusy;
-    await request({
-      url: '/dyaw/xlfw_zxhd/get_user_status',
-      data: {
-        user_id: otherInfo?.id
-      }
-    }).then(res => {
-      if (res.code === '1') {
-        isBusy = !!res.data.status
-      }
-    })
-    if (isBusy) {
-      // busy operation
-      currentChatStatus = CHAT_STATUS.WAITING_BUSY
-      ElMessage.warning('对方忙线中')
-      setTimeout(() => {
-        handleClose()
-      }, 2000)
-      return
-    };
-    await AgoraRTC.getMicrophones()
-    if (mode === 'video')
-      await AgoraRTC.getCameras()
-    initRtcClient()
-
-    await getRtcOption()
-
-    const fullSendData = await handleInfoAdd('拨号中')
-
-
-    await nextTick(async () => {
-      await rtcInstance.client!.join(rtcOptions.appId, rtcOptions.channel, rtcOptions?.token, /*rtcOptions.uid*/ user.user_id);
-      rtcInstance.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
-      // await rtcInstance.client!.publish(rtcInstance.localAudioTrack);
-      if (mode === 'video') {
-        rtcInstance.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
-        rtcInstance.localVideoTrack.play(LocalPlayerContainerRef as HTMLElement);
-        // await rtcInstance.client!.publish(rtcInstance.localVideoTrack);
-      }
-
-      socketSend(ws2, {
-        dxzl_stu_user_id: dyaw_xlfw_zxhd!.dxz_stu_user_id,
-        dxzl_tea_user_id: dyaw_xlfw_zxhd!.dxz_tea_user_id,
-        operate: CHAT_OPERATION.START,
-        mode,
-        // rtcOptions,
-        dyaw_xlfw_zxhd,
-        fullSendData,
-        dxzl_id: fullSendData?.dxzl_id
-      })
-    })
-
-  } catch (error) {
-    console.error(error);
-    handleClose()
-  }
-
-}
-async function handleAudioChatCancel() {
-  const fullSendData = await handleInfoEdit({ dxzl_status: '2' }, '已取消')
-  socketSend(ws2, {
-    dxzl_stu_user_id: dyaw_xlfw_zxhd!.dxz_stu_user_id,
-    dxzl_tea_user_id: dyaw_xlfw_zxhd!.dxz_tea_user_id,
-    operate: CHAT_OPERATION.CANCEL,
-    fullSendData
-  })
-  isOpen = false
-  rtcInstance.client?.leave();
-  handleSwitchUse(false)
-
-}
-async function handleAudioChatAccept() {
-  currentChatStatus = CHAT_STATUS.WAITING_ACCEPT
-
-
-  const fullSendData = await handleInfoEdit({ dxzl_status: '3' }, '通话中')
-  // ...
-  await getRtcOption()
-  initRtcClient()
-  await rtcInstance.client!.join(rtcOptions.appId, rtcOptions.channel, rtcOptions?.token, /*rtcOptions.uid*/ user.user_id);
-  rtcInstance.localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
-  await rtcInstance.client!.publish(rtcInstance.localAudioTrack);
-  if (mode === 'video') {
-    rtcInstance.localVideoTrack = await AgoraRTC.createCameraVideoTrack();
-    rtcInstance.localVideoTrack.play(LocalPlayerContainerRef as HTMLElement);
-    await rtcInstance.client!.publish(rtcInstance.localVideoTrack);
-  }
-  //
-  socketSend(ws2, {
-    dxzl_stu_user_id: dyaw_xlfw_zxhd!.dxz_stu_user_id,
-    dxzl_tea_user_id: dyaw_xlfw_zxhd!.dxz_tea_user_id,
-    operate: CHAT_OPERATION.ACCEPT,
-    fullSendData
-  })
-  currentChatStatus = CHAT_STATUS.CHATING
-  offsetTime = 0
-  offsetTimer = setInterval(() => {
-    offsetTime = (offsetTime as number + 1)
-  }, 1000)
-}
-async function handleAudioChatDeny() {
-  const fullSendData = await handleInfoEdit({ dxzl_status: '4' }, '已拒接')
-  socketSend(ws2, {
-    dxzl_stu_user_id: dyaw_xlfw_zxhd!.dxz_stu_user_id,
-    dxzl_tea_user_id: dyaw_xlfw_zxhd!.dxz_tea_user_id,
-    operate: CHAT_OPERATION.DENY,
-    fullSendData
-  })
-  isOpen = false
-}
-async function handleAudioChatEnd() {
-  const fullSendData = await handleInfoEdit({ dxzl_status: '4' }, /*'已结束'*/formatOffsetSec(offsetTime))
-  offsetTimer && clearInterval(offsetTimer)
-  await rtcInstance.client!.leave();
-  handleSwitchUse(false)
-
-  socketSend(ws2, {
-    dxzl_stu_user_id: dyaw_xlfw_zxhd!.dxz_stu_user_id,
-    dxzl_tea_user_id: dyaw_xlfw_zxhd!.dxz_tea_user_id,
-    operate: CHAT_OPERATION.END,
-    fullSendData
-  })
-  isOpen = false
-}
-
-let audioInUse = $ref(true)
-let videoInUse = $ref(true)
-
-function handleSwitchAudio(v?: boolean) {
-  audioInUse = v ?? !audioInUse
-  try {
-    rtcInstance.localAudioTrack!.setEnabled(audioInUse)
-  } catch (error) {
-    console.error(error);
-  }
-
-}
-function handleSwitchVideo(v?: boolean) {
-  videoInUse = v ?? !videoInUse
-  try {
-    rtcInstance.localVideoTrack!.setEnabled(videoInUse)
-  } catch (error) {
-    console.error(error);
-  }
-}
-
-
-function handleCloseForce() {
-  if (currentChatStatus === CHAT_STATUS.CHATING) {
-    handleAudioChatEnd()
-  }
-  else {
-    handleAudioChatCancel()
-  }
-}
-
-let mins = $ref(false)
-let isMove = $ref(false)
-let isDown = $ref(false)
-function handleChangeMins() {
-  isDown = false
-  if (isMove) {
-    isMove = false;
-    return;
-  }
-  mins = !mins
-}
-
-</script>
-
-<template>
-  <UseDraggable v-if="isOpen" storage-key="chat-audio" storage-type="session" :initial-value="{ x: 584, y: 207 }"
-    class="fixed bg-transparent cursor-move z-4000">
-    <div v-show="mins" class="w-375px h-0 flex justify-end">
-      <el-avatar :size="90" :src="otherInfo?.avatar" @mousedown="isDown = true" @mousemove="isMove = isDown"
-        @click="handleChangeMins"></el-avatar>
-    </div>
-    <div class="w-375px h-670px bg-hex-191919" v-show="!mins">
-      <div class="w-full flex justify-end items-center p-2 text-light-50 h-36px">
-        <i:clarity:window-min-line class="cursor-pointer" @click="handleChangeMins" />
-        <i:ic:outline-close class="cursor-pointer" @click="handleCloseForce" />
-      </div>
-      <div class="h-634px relative">
-        <slot>
-
-          <div v-show="mode === 'video' && currentChatStatus === CHAT_STATUS.CHATING" ref="LocalPlayerContainerRef"
-            class="absolute z-6 w-144px h-174px top-80px right-10px border border-dark-50"></div>
-          <div v-show="mode === 'video' && currentChatStatus === CHAT_STATUS.CHATING" ref="RemotePlayerContainerRef"
-            class="absolute z-1 inset-0"></div>
-
-
-          <div class="absolute z-11 left-0 bottom-66px text-xl flex justify-around w-full text-light-50 ">
-            <div v-show="currentChatStatus === CHAT_STATUS.WAITING_YOU_ACCEPT"
-              class="bg-green-600 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleAudioChatAccept">
-              <i:ic:baseline-phone />
-            </div>
-            <div v-show="currentChatStatus === CHAT_STATUS.WAITING_YOU_ACCEPT"
-              class="bg-red-600 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleAudioChatDeny">
-              <i:mdi:phone-hangup />
-            </div>
-            <div v-show="currentChatStatus === CHAT_STATUS.CHATING"
-              class="bg-hex-efefef text-hex-272636 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleSwitchAudio()">
-              <i:ant-design:audio-outlined v-show="audioInUse" />
-              <i:ant-design:audio-muted-outlined v-show="!audioInUse" />
-            </div>
-            <div v-show="currentChatStatus === CHAT_STATUS.CHATING"
-              class="bg-red-600 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleAudioChatEnd">
-              <i:ic:outline-close></i:ic:outline-close>
-            </div>
-            <div
-              v-show="currentChatStatus === CHAT_STATUS.WAITING_OTHERS_ACCEPT || currentChatStatus === CHAT_STATUS.WAITING_BUSY"
-              class="bg-red-600 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleAudioChatCancel">
-              <i:ic:outline-close></i:ic:outline-close>
-            </div>
-            <div v-show="mode === 'video' && currentChatStatus === CHAT_STATUS.CHATING"
-              class="bg-hex-efefef text-hex-272636 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
-              @click="handleSwitchVideo()">
-              <i:material-symbols:video-camera-back-rounded v-show="videoInUse" />
-              <i:material-symbols:video-camera-front-off-rounded v-show="!videoInUse" />
-            </div>
-          </div>
-          <!--  -->
-          <div class="pt-24 flex_center flex-col text-light-50 space-y-4">
-            <!-- <div>{{ CHAT_STATUS[currentChatStatus] }}</div> -->
-            <el-avatar v-show="mode === 'audio' || (mode === 'video' && currentChatStatus !== CHAT_STATUS.CHATING)"
-              :size="158" :src="otherInfo?.avatar"></el-avatar>
-            <div v-show="mode === 'audio' || (mode === 'video' && currentChatStatus !== CHAT_STATUS.CHATING)">{{
-              otherInfo?.realname
-            }}</div>
-            <!-- <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"> -->
-            <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"
-              v-show="currentChatStatus === CHAT_STATUS.WAITING_OTHERS_ACCEPT">
-              <div>正在等待对方接受邀请</div>
-              <i:line-md:loading-alt-loop class="text-xl" />
-            </div>
-            <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"
-              v-show="currentChatStatus === CHAT_STATUS.WAITING_YOU_ACCEPT">
-              <div>邀请你语音通话...</div>
-            </div>
-            <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"
-              v-show="currentChatStatus === CHAT_STATUS.WAITING_BUSY">
-              <div class="text-red-500">对方忙线中</div>
-              <div class="text-red-500">请稍后再试</div>
-              <i:line-md:loading-alt-loop class="text-xl" />
-            </div>
-            <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"
-              v-show="currentChatStatus === CHAT_STATUS.WAITING_ACCEPT">
-              <div>接通中...</div>
-            </div>
-            <div class="text-hex-909090 flex_center flex-col space-y-2 h-16"
-              v-show="mode === 'audio' && currentChatStatus === CHAT_STATUS.CHATING">
-              <div>正在通话中</div>
-              <div>{{ formatOffsetSec(offsetTime) }}</div>
-            </div>
-            <!-- </div> -->
-          </div>
-        </slot>
-      </div>
-    </div>
-
-  </UseDraggable>
-</template>
-

+ 0 - 184
src/components/stu-doc-item/index.vue

@@ -1,184 +0,0 @@
-<script setup>
-import { ref, reactive, defineProps,defineEmits ,toRef } from "vue";
-import { Plus, Calendar, Search } from "@element-plus/icons-vue";
-import { ElMessage, ElMessageBox } from "element-plus";
-
-
-const optionRadio = ref(props.docInfo.dxxd_wxdj);
-
-const props = defineProps(['docInfo', 'd'])
-
-const emit = defineEmits(['func']);
-
-const checkedIn = ref(false);
-const chekboxShow = (val) => {
-  let objA = {
-      mark: val,
-      params:props.docInfo.dxxd_id,
-    }
-    emit('editChooseArr', objA)
-}
-
-</script>
-<template>
-  <div class="singleAll">
-    <div class="withAllShow">
-      <div class="withAllShow_left">
-        <el-checkbox v-model="checkedIn" @change="chekboxShow"></el-checkbox>
-      </div>
-      <div class="singleItem">
-        <div class="everyRow flexEveryRow">
-          <div class="mainT">咨询记录</div>
-          <div class="dataShow">日期:{{ props.docInfo.dxxd_date }}</div>
-        </div>
-        <div class="everyRow moreDetail">
-          <div class="vistorFirstShow">来访者姓名</div>
-          <div class="vistorSecondFill">{{ props.docInfo.dxxd_lfzxm }}</div>
-          <div class="vistorFirstShow">学校</div>
-          <div class="vistorSecondFill" style="width: 25%">{{ props.docInfo.dxxd_school_name }}</div>
-          <div class="vistorFirstShow">班级</div>
-          <div class="vistorSecondFill" style="width: 15%; border-right: 0">{{ props.docInfo.dxxd_class_name }}</div>
-        </div>
-        <div class="everyRow moreDetail">
-          <div class="vistorFirstShow">接访老师</div>
-          <div class="vistorSecondFill">{{ props.docInfo.dxxd_jfls }}</div>
-          <div class="vistorFirstShow">住址(选填)</div>
-          <div class="vistorSecondFill" style="width: 25%"> {{ props.docInfo.dxxd_address }} </div>
-          <div class="vistorFirstShow">电话(选填)</div>
-          <div class="vistorSecondFill" style="width: 15%; border-right: 0">{{ props.docInfo.dxxd_phone }}</div>
-        </div>
-        <div class="everyRow textAreaCont">
-          <P class="MainP">咨询缘由:</P>
-          <div class="detailShow">{{ props.docInfo.dxxd_zxyy }}</div>
-        </div>
-        <div class="everyRow textAreaCont">
-          <P class="MainP">来访者自述:</P>
-          <div class="detailShow">{{ props.docInfo.dxxd_lfzzs }}</div>
-        </div>
-        <div class="everyRow textAreaCont">
-          <P class="MainP">初步分析:</P>
-          <div class="detailShow">{{ props.docInfo.dxxd_cbfx }}</div>
-        </div>
-        <div class="everyRow textAreaCont">
-          <P class="MainP">危险等级(单选):</P>
-          <div class="detailShow">
-            <el-radio-group v-model="optionRadio" class="ml-4" :disabled="d">
-              <el-radio label="1" size="small">一般</el-radio>
-              <el-radio label="2" size="small">严重</el-radio>
-              <el-radio label="3" size="small">重大</el-radio>
-            </el-radio-group>
-          </div>
-        </div>
-        <div class="everyRow textAreaCont">
-          <P class="MainP">处理方式:</P>
-          <div class="detailShow">{{ props.docInfo.dxxd_clfs }}</div>
-        </div>
-      </div>
-    </div>
-    <div class="ZXTime">咨询时长:{{ props.docInfo.xxd_zxsc }}</div>
-  </div>
-</template>
-
-<style scoped>
-.withAllShow{
-  display: flex;
-}
-.withAllShow_left {
-  width: 2%;
-  margin-right: 1%;
-}
-.singleItem {
-  width: 97%;
-  border: 1px solid #969696;
-}
-
-.everyRow {
-  border: 1px solid #dbdbdb;
-}
-
-.flexEveryRow {
-  display: flex;
-  flex-direction: row;
-}
-
-.mainT {
-  width: 65%;
-  height: 36px;
-  line-height: 36px;
-  font-size: 16px;
-  font-family: PingFangSC-Regular;
-  text-align: left;
-  color: #000000;
-  box-sizing: border-box;
-  padding: 0 0 0 10%;
-  background: url("../assets/images/cardIcon.png") no-repeat 10% center;
-  background-size: 19px 18px;
-}
-
-.dataShow {
-  width: 35%;
-  height: 36px;
-  line-height: 36px;
-  font-size: 12px;
-  font-family: PingFangSC-Regular;
-  text-align: left;
-  color: #898989;
-}
-
-.moreDetail {
-  display: flex;
-  flex-direction: row;
-  height: 32px;
-  line-height: 32px;
-}
-
-.vistorFirstShow {
-  width: 15%;
-  font-size: 10px;
-  font-family: PingFangSC-Regular;
-  text-align: left;
-  color: #000000;
-  border-right: 1px solid #dbdbdb;
-  box-sizing: border-box;
-  padding: 0 0 0 5px;
-}
-
-.vistorSecondFill {
-  font-size:10px;
-  width: 15%;
-  border-right: 1px solid #dbdbdb;
-  box-sizing: border-box;
-  padding: 0 0 0 2px;
-}
-
-.textAreaCont {
-  height: 69px;
-  box-sizing: border-box;
-  padding-left: 3px;
-  display: flex;
-  flex-direction: column;
-}
-
-.MainP {
-  height: 15px;
-  font-size: 10px;
-  font-family: PingFangSC-Regular;
-  text-align: left;
-  color: #000000;
-}
-
-.detailShow {
-  font-size: 14px;
-  color: #969696;
-}
-
-.ZXTime {
-  height: 40px;
-  line-height: 40px;
-  font-size: 16px;
-  font-family: PingFangSC-Regular;
-  text-align: left;
-  color: #007dff;
-  margin-left:3%;
-}
-</style>

+ 0 - 41
src/components/teacher-card/index.vue

@@ -1,41 +0,0 @@
-<script setup lang="ts">
-import { ElMessage } from 'element-plus'
-
-const props = defineProps<{ d: any }>()
-
-const router = useRouter()
-
-function handleClickConsult() {
-  if (!props.d.user_id) {
-    console.error('用户未认证')
-    return ElMessage({ message: '老师正忙,请稍后再试', type: 'warning' })
-  }
-  sessionStorage.setItem('consult_teacher', JSON.stringify(props.d))
-  router.push({ name: 'student_consult' })
-}
-</script>
-
-<template>
-  <div class="bg-white w-370px h-160px rounded-md box_shadow flex flex-col justify-between p-4">
-    <div class="flex items-center justify-between">
-      <div class="flex space-x-4 items-center">
-        <el-avatar :size="48" :src="d.dxp_user_avatar"></el-avatar>
-        <div class="text-lg max-w-24 truncate">{{ d.dxp_user_realname }}</div>
-      </div>
-      <slot>
-        <el-button type="primary" round @click="handleClickConsult">咨询</el-button>
-      </slot>
-
-    </div>
-    <div class="text-hex-5B9FF0">联系电话:{{ d.dxp_user_phone }}</div>
-    <div class="line-clamp-2 overflow-ellipsis text-hex-00000041 h-10 leading-5">
-      {{ d.dxp_jj }}
-    </div>
-  </div>
-</template>
-
-<style scoped>
-.box_shadow {
-  box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.04);
-}
-</style>

+ 144 - 0
src/pages/admin/back/ctfx.vue

@@ -0,0 +1,144 @@
+<script setup lang='ts'>
+import { Search } from '@element-plus/icons-vue'
+import { useRouter, RouteLocationRaw } from 'vue-router'
+import { Grade, Subject, Status } from '~/store/info'
+import user from '~/store/user'
+
+const router = useRouter()
+function routerPush(_route: RouteLocationRaw) {
+  router.push(_route)
+}
+
+const tableData = ref([])
+const total = ref(0)
+const multipleSelection = ref([])
+const handleSelectionChange = (val) => {
+  multipleSelection.value = val
+}
+
+const queryForm = reactive({
+  dc_grade_id: undefined,
+  dc_subject_id: undefined,
+  dc_check: undefined,
+  dc_keyword: undefined,
+  page: 1
+})
+
+watch(queryForm, () => {
+  doQuery()
+}, { deep: true, immediate: true })
+
+function doQuery() {
+  request({
+    url: '/dyaw/ctfx/index',
+    data: {
+      ...queryForm,
+      sm_id: user.sm_info.sm_id,
+      create_user_id: user.user_id
+    }
+  }).then(res => {
+    if (res.code === "1") {
+      tableData.value = res.data.page_data
+      total.value = parseInt(res.data.total_rows)
+    }
+  })
+}
+
+function doDelete() {
+  request({
+    url: '/dyaw/ctfx/delete',
+    data: {
+      dc_id: multipleSelection.value.map(item => item.dc_id)
+    }
+  }).then(
+    res => {
+      if (res.code === "1") {
+        ElMessage.success('删除成功')
+        doQuery()
+      }
+    }
+  )
+}
+
+
+function formatTime(s: string) {
+  return (new Date(s * 1000)).toLocaleString()
+}
+
+</script>
+
+<template>
+  <div class="w-1400px h-640px flex flex-col">
+    <div class="flex justify-end items-center">
+      <div class="flex space-x-2">
+        <el-select clearable size="large" placeholder="年级" v-model="queryForm.dc_grade_id">
+          <el-option v-for="({ grade_id, grade_name }) in Grade" :key="grade_id" :label="grade_name" :value="grade_id" />
+        </el-select>
+        <el-select clearable size="large" placeholder="科目" v-model="queryForm.dc_subject_id">
+          <el-option v-for="({ subject_id, subject_name }) in Subject" :key="subject_id" :label="subject_name"
+            :value="subject_id" />
+        </el-select>
+        <el-select clearable size="large" placeholder="状态" v-model="queryForm.dc_check">
+          <el-option v-for="(k) in Object.keys(Status)" :key="k" :label="Status[k]" :value="k" />
+        </el-select>
+        <el-input size="large" placeholder="关键字搜索" class="max-w-240px" :suffix-icon="Search"
+          v-model="queryForm.dc_keyword"></el-input>
+        <el-button size="large" type="success" @click="routerPush({ name: 'teacher_indey_create' })">新建</el-button>
+        <el-button size="large" type="danger" @click="doDelete">删除</el-button>
+      </div>
+    </div>
+
+    <div class="flex-auto flex-col flex mt-4 justify-between">
+      <el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" />
+        <el-table-column property="dc_title" label="标题" width="auto" show-overflow-tooltip />
+        <el-table-column property="dc_grade_name" label="年级" width="120" />
+        <el-table-column property="dc_subject_name" label="科目" width="120" />
+        <el-table-column property="dc_keyword" label="关键词" width="120" />
+        <el-table-column property="create_user_realname" label="上传教师" width="120" />
+        <el-table-column property="create_dateline" label="上传时间" width="180">
+          <template #default="{ row }">
+            <span>{{ formatTime(row.create_dateline) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column property="name" label="状态" width="120">
+          <template #default="{ row }">
+            <span>{{ Status[row.dc_check] }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right" label="操作" width="120">
+          <template #default="{ row }">
+            <el-button link type="primary" size="small"
+              @click="routerPush({ name: 'teacher_indey_detail', params: { id: row.dc_id } })">查看</el-button>
+            <el-button link type="primary" size="small" :disabled="row.dc_check !== '0'"
+              @click="routerPush({ name: 'teacher_indey_edit', params: { id: row.dc_id } })">编辑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="flex justify-end mt-6">
+        <el-pagination v-model:current-page="queryForm.page" background layout="prev, pager, next"
+          :total="total"></el-pagination>
+      </div>
+
+    </div>
+  </div>
+</template>
+
+
+<style scoped lang="scss">
+.tab_selected {
+  color: #007DFF;
+
+  &::after {
+    content: '-';
+    color: transparent;
+    position: absolute;
+    left: -5px;
+    right: -5px;
+    bottom: -4px;
+    height: 15px;
+    background: rgba(0, 125, 255, 0.30);
+    border-radius: 8px;
+  }
+}
+</style>

+ 0 - 32
src/pages/admin/home.vue

@@ -1,32 +0,0 @@
-<script setup lang="ts">
-const imgSrc = (await request({
-  url: '/dyaw/xlfw_hbgl/index',
-  data: {
-    limit: 1
-  }
-})).data?.page_data?.[0]?.dxh_path
-const teacherList = (await request({
-  url: '/dyaw/xlfw_pbgl_pb/index',
-  data: {
-    dxpp_date: Date.now()
-  }
-})).data?.page_data
-
-</script>
-
-<template>
-  <div class="h-640px bg-hex-f2f2f295 flex justify-center">
-    <div class="w-1000px h-full overflow-hidden">
-      <img v-show="imgSrc" :src="imgSrc" class="w-full h-full max-h-640px object-contain">
-    </div>
-    <div
-      class="w-400px h-full overflow-y-auto flex flex-col items-center space-y-4 py-4 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
-      <el-empty v-if="!teacherList?.length" description="今天暂无排班老师"></el-empty>
-      <teacher-card v-for="item in teacherList" class="flex-none" :d="item">
-        <el-tag :type="item.online ? 'success' : 'danger'" >
-          {{ item.online ? '在 线' : '离 线' }}
-        </el-tag>
-      </teacher-card>
-    </div>
-  </div>
-</template>

+ 3 - 18
src/pages/student/detail.vue

@@ -19,21 +19,6 @@ const router = useRouter()
     </div>
 
 
-    <div class="space-y-4 mt-8">
-      <div class="flex justify-between">
-        <div class="text-xl whitespace-nowrap overflow-ellipsis overflow-hidden w-max-420px">高三数学精优我是标题文字</div>
-        <el-tag>关键字</el-tag>
-      </div>
-      <div class="flex space-x-6 text-gray-500">
-        <div>三年级</div>
-        <div>语文</div>
-        <div>张美丽</div>
-        <div>2021/12/22 12:22</div>
-      </div>
-
-      <div class=" text-gray-500 pt-4">
-        曾经靠“我为自己代言”广告词崭露头角的陈欧在2016年进军影视文化行业,2017年进军空气净化器行业,又在同年四月份宣布开始共享充电宝业务,陈欧三陈欧三陈欧三。欧三陈欧三次次次跨界次跨界2017年进军空气净化器行业,又在同年四月份宣布开始共享充电宝业务,陈欧三陈欧三陈欧三次跨界2017年进军空气净化器行业,又在同年四月份宣布。
-        开始共享充电宝业务,陈欧三陈次跨界2017年进军空气净化器行业,又在同年四月份宣布开始共享充电宝业务,陈欧三陈欧三陈欧三次跨界2017年进军空气净化器行业,又在同年四月份宣布。
-        开始共享充电宝业务,陈欧三陈欧三陈欧三次跨界三次跨界三次跨界三次跨界三次跨界</div>
-    </div>
-  </div></template>
+    <cuoti-detail />
+  </div>
+</template>

+ 4 - 4
src/pages/student/index.vue

@@ -16,11 +16,11 @@ function routerPush(_route: RouteLocationRaw) {
   <div class="w-1400px h-640px flex flex-col">
     <div class="flex justify-between items-center">
       <div class="flex flex-none space-x-12 text-lg h-50px">
-        <div class="cursor-pointer relative mb-22px selected">错题列表</div>
+        <div class="cursor-pointer relative mb-22px tab_selected">错题列表</div>
       </div>
       <div class="flex space-x-2">
-        <el-select size="large" placeholder="年级"></el-select>
-        <el-select size="large" placeholder="科目"></el-select>
+        <el-select clearable size="large" placeholder="年级"></el-select>
+        <el-select clearable size="large" placeholder="科目"></el-select>
         <el-input size="large" placeholder="关键字搜索" class="w-200px" :suffix-icon="Search"></el-input>
       </div>
     </div>
@@ -52,7 +52,7 @@ function routerPush(_route: RouteLocationRaw) {
 
 
 <style scoped lang="scss">
-.selected {
+.tab_selected {
   color: #007DFF;
 
   &::after {

+ 16 - 8
src/pages/teacher/index.vue

@@ -1,6 +1,9 @@
 <script setup lang='ts'>
 import { Search } from '@element-plus/icons-vue'
 import { useRouter, RouteLocationRaw } from 'vue-router'
+import { Grade, Subject } from '~/store/info'
+
+
 const list = [
   { title: '错题列表', route: '/' },
 ]
@@ -16,22 +19,27 @@ function routerPush(_route: RouteLocationRaw) {
   <div class="w-1400px h-640px flex flex-col">
     <div class="flex justify-between items-center">
       <div class="flex flex-none space-x-12 text-lg h-50px">
-        <div class="cursor-pointer relative mb-22px selected">错题列表</div>
-        <div class="cursor-pointer relative mb-22px" @click="routerPush({name:'teacher_indey'})">上传错题</div>
+        <div class="cursor-pointer relative mb-22px tab_selected">错题列表</div>
+        <div class="cursor-pointer relative mb-22px" @click="routerPush({ name: 'teacher_indey' })">上传错题</div>
       </div>
       <div class="flex space-x-2">
-        <el-select size="large" placeholder="年级"></el-select>
-        <el-select size="large" placeholder="科目"></el-select>
-        <el-input size="large" placeholder="关键字搜索" class="w-200px" :suffix-icon="Search"></el-input>
+        <el-select clearable size="large" placeholder="年级">
+          <el-option v-for="({ grade_id, grade_name }) in Grade" :key="grade_id" :label="grade_name" :value="grade_id" />
+        </el-select>
+        <el-select clearable size="large" placeholder="科目">
+          <el-option v-for="({ subject_id, subject_name }) in Subject" :key="subject_id" :label="subject_name"
+            :value="subject_id" />
+        </el-select>
+        <el-input size="large" placeholder="关键字搜索" class="max-w-240px" :suffix-icon="Search"></el-input>
       </div>
     </div>
 
     <div class="flex-auto flex-col flex mt-4  justify-between">
       <div class="grid grid-cols-2 grid-rows-4 gap-4">
         <div class="cursor-pointer rounded shadow px-4 py-2 h-120px space-y-2" v-for="i in 8"
-          @click="routerPush({ name: 'student_detail', params: { id: i } })">
+          @click="routerPush({ name: 'teacher_index_detail', params: { id: i } })">
           <div class="flex justify-between">
-            <div class="text-xl whitespace-nowrap overflow-ellipsis overflow-hidden w-max-420px">高三数学精优我是标题文字</div>
+            <div class="text-xl whitespace-nowrap overflow-ellipsis overflow-hidden max-w-420px">高三数学精优我是标题文字</div>
             <el-tag>关键字</el-tag>
           </div>
           <div class="flex space-x-6 text-gray-500">
@@ -53,7 +61,7 @@ function routerPush(_route: RouteLocationRaw) {
 
 
 <style scoped lang="scss">
-.selected {
+.tab_selected {
   color: #007DFF;
 
   &::after {

+ 24 - 0
src/pages/teacher/index/detail.vue

@@ -0,0 +1,24 @@
+<script setup lang='ts'>
+const router = useRouter()
+</script>
+
+<template>
+  <div class="w-1400px h-640px flex flex-col">
+    <div class="flex justify-between">
+      <div class="space-x-2 text-blue-700 flex items-center">
+        <i:cil:home />
+        <span>首页</span>
+        <span>/</span>
+        <span>错题分析</span>
+        <span>/</span>
+        <span>错题列表</span>
+        <span>/</span>
+        <span class="text-gray-600">详情</span>
+      </div>
+      <el-button type="primary" @click="router.back()">返回</el-button>
+    </div>
+
+
+    <cuoti-detail />
+  </div>
+</template>

+ 93 - 20
src/pages/teacher/indey.vue

@@ -1,6 +1,9 @@
 <script setup lang='ts'>
 import { Search } from '@element-plus/icons-vue'
 import { useRouter, RouteLocationRaw } from 'vue-router'
+import { Grade, Subject, Status } from '~/store/info'
+import user from '~/store/user'
+
 const list = [
   { title: '错题列表', route: '/' },
 ]
@@ -10,10 +13,61 @@ function routerPush(_route: RouteLocationRaw) {
 }
 
 const tableData = ref([])
+const total = ref(0)
 const multipleSelection = ref([])
 const handleSelectionChange = (val) => {
   multipleSelection.value = val
 }
+
+const queryForm = reactive({
+  dc_grade_id: undefined,
+  dc_subject_id: undefined,
+  dc_check: undefined,
+  dc_keyword: undefined,
+  page: 1
+})
+
+watch(queryForm, () => {
+  doQuery()
+}, { deep: true, immediate: true })
+
+function doQuery() {
+  request({
+    url: '/dyaw/ctfx/index',
+    data: {
+      ...queryForm,
+      sm_id: user.sm_info.sm_id,
+      create_user_id: user.user_id
+    }
+  }).then(res => {
+    if (res.code === "1") {
+      tableData.value = res.data.page_data
+      total.value = parseInt(res.data.total_rows)
+    }
+  })
+}
+
+function doDelete() {
+  request({
+    url: '/dyaw/ctfx/delete',
+    data: {
+      dc_id: multipleSelection.value.map(item => item.dc_id)
+    }
+  }).then(
+    res => {
+      if (res.code === "1") {
+        ElMessage.success('删除成功')
+        doQuery()
+      }
+    }
+  )
+}
+
+
+function formatTime(s: string) {
+  return (new Date(s * 1000)).toLocaleString()
+}
+
 </script>
 
 <template>
@@ -21,37 +75,56 @@ const handleSelectionChange = (val) => {
     <div class="flex justify-between items-center">
       <div class="flex flex-none space-x-12 text-lg h-50px">
         <div class="cursor-pointer relative mb-22px" @click="routerPush({ name: 'teacher_index' })">错题列表</div>
-        <div class="cursor-pointer relative mb-22px selected">上传错题</div>
+        <div class="cursor-pointer relative mb-22px tab_selected">上传错题</div>
       </div>
       <div class="flex space-x-2">
-        <el-select size="large" placeholder="年级"></el-select>
-        <el-select size="large" placeholder="科目"></el-select>
-        <el-select size="large" placeholder="状态"></el-select>
-        <el-input size="large" placeholder="关键字搜索" class="w-200px" :suffix-icon="Search"></el-input>
-        <el-button size="large" type="success" @click="routerPush({name:'teacher_create'})" >新建</el-button>
-        <el-button size="large" type="danger">删除</el-button>
+        <el-select clearable size="large" placeholder="年级" v-model="queryForm.dc_grade_id">
+          <el-option v-for="({ grade_id, grade_name }) in Grade" :key="grade_id" :label="grade_name" :value="grade_id" />
+        </el-select>
+        <el-select clearable size="large" placeholder="科目" v-model="queryForm.dc_subject_id">
+          <el-option v-for="({ subject_id, subject_name }) in Subject" :key="subject_id" :label="subject_name"
+            :value="subject_id" />
+        </el-select>
+        <el-select clearable size="large" placeholder="状态" v-model="queryForm.dc_check">
+          <el-option v-for="(k) in Object.keys(Status)" :key="k" :label="Status[k]" :value="k" />
+        </el-select>
+        <el-input size="large" placeholder="关键字搜索" class="max-w-240px" :suffix-icon="Search"
+          v-model="queryForm.dc_keyword"></el-input>
+        <el-button size="large" type="success" @click="routerPush({ name: 'teacher_indey_create' })">新建</el-button>
+        <el-button size="large" type="danger" @click="doDelete">删除</el-button>
       </div>
     </div>
 
     <div class="flex-auto flex-col flex mt-4 justify-between">
       <el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" />
-        <el-table-column label="标题" width="auto" show-overflow-tooltip />
-        <el-table-column property="name" label="年级" width="120" />
-        <el-table-column property="name" label="科目" width="120" />
-        <el-table-column property="name" label="关键词" width="120" />
-        <el-table-column property="name" label="上传教师" width="120" />
-        <el-table-column property="name" label="上传时间" width="120" />
-        <el-table-column property="name" label="状态" width="120" />
-        <el-table-column fixed="right" label="操作" width="200">
-          <template #default>
-            <el-button link type="primary" size="small">查看 </el-button>
-            <el-button link type="primary" size="small">编辑</el-button>
+        <el-table-column property="dc_title" label="标题" width="auto" show-overflow-tooltip />
+        <el-table-column property="dc_grade_name" label="年级" width="120" />
+        <el-table-column property="dc_subject_name" label="科目" width="120" />
+        <el-table-column property="dc_keyword" label="关键词" width="120" />
+        <el-table-column property="create_user_realname" label="上传教师" width="120" />
+        <el-table-column property="create_dateline" label="上传时间" width="180">
+          <template #default="{ row }">
+            <span>{{ formatTime(row.create_dateline) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column property="name" label="状态" width="120">
+          <template #default="{ row }">
+            <span>{{ Status[row.dc_check] }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column fixed="right" label="操作" width="120">
+          <template #default="{ row }">
+            <el-button link type="primary" size="small"
+              @click="routerPush({ name: 'teacher_indey_detail', params: { id: row.dc_id } })">查看</el-button>
+            <el-button link type="primary" size="small" :disabled="row.dc_check !== '0'"
+              @click="routerPush({ name: 'teacher_indey_edit', params: { id: row.dc_id } })">编辑</el-button>
           </template>
         </el-table-column>
       </el-table>
       <div class="flex justify-end mt-6">
-        <el-pagination background layout="prev, pager, next" :total="1000"></el-pagination>
+        <el-pagination v-model:current-page="queryForm.page" background layout="prev, pager, next"
+          :total="total"></el-pagination>
       </div>
 
     </div>
@@ -60,7 +133,7 @@ const handleSelectionChange = (val) => {
 
 
 <style scoped lang="scss">
-.selected {
+.tab_selected {
   color: #007DFF;
 
   &::after {

+ 55 - 60
src/pages/teacher/create.vue

@@ -3,6 +3,9 @@ import { useRouter, RouteLocationRaw } from 'vue-router'
 import type { FormInstance, FormRules } from 'element-plus'
 import { ref, reactive } from 'vue'
 import { Plus } from '@element-plus/icons-vue'
+import { Grade, Subject } from '~/store/info'
+import user from '~/store/user'
+const token = user.token
 
 import type { UploadProps, UploadUserFile } from 'element-plus'
 
@@ -11,6 +14,11 @@ function routerPush(_route: RouteLocationRaw) {
   router.push(_route)
 }
 
+const UploadAction = window.GLOBAL_CONFIG.oss + '/upload/main/file'
+function handleUploadSuccess(res: any, file: UploadUserFile, fileList: UploadUserFile[]) {
+  file.p_url = '/' + res.data.url
+}
+
 const ruleFormRef = ref<FormInstance>()
 const ruleForm = reactive({
   dc_title: '',
@@ -22,68 +30,52 @@ const ruleForm = reactive({
 })
 
 const rules = reactive<FormRules>({
-  name: [
-    { required: true, message: 'Please input name', trigger: 'blur' },
-    { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },
-  ],
-  region: [
-    {
-      required: true,
-      message: 'Please select zone',
-      trigger: 'change',
-    },
-  ],
-  count: [
-    {
-      required: true,
-      message: 'Please select count',
-      trigger: 'change',
-    },
-  ],
-  date1: [
-    {
-      type: 'date',
-      required: true,
-      message: 'Please pick a date',
-      trigger: 'change',
-    },
-  ],
-  date2: [
-    {
-      type: 'date',
-      required: true,
-      message: 'Please pick a time',
-      trigger: 'change',
-    },
-  ],
-  type: [
-    {
-      type: 'array',
-      required: true,
-      message: 'Please select at least one type',
-      trigger: 'change',
-    },
-  ],
-  resource: [
-    {
-      required: true,
-      message: 'Please select resource',
-      trigger: 'change',
-    },
-  ],
-  desc: [
-    { required: true, message: 'Please input form', trigger: 'blur' },
-  ],
+  dc_title: [{ required: true, message: '标题不能为空', trigger: 'blur', }],
+  dc_grade_id: [{ required: true, message: '年级不能为空', trigger: 'blur', }],
+  dc_subject_id: [{ required: true, message: '科目不能为空', trigger: 'blur', }],
+  dc_keyword: [{ required: true, message: '关键字不能为空', trigger: 'blur', }],
 })
 
+const submit_loading = ref(false)
 const submitForm = async (formEl: FormInstance | undefined) => {
   console.log('ruleForm : ', ruleForm)
   if (!formEl) return
   await formEl.validate((valid, fields) => {
     if (valid) {
+      submit_loading.value = true
       console.log('submit!')
+      const dyaw_ctfx = Object.assign({}, ruleForm, {
+        dc_grade_name: Grade.find((item) => item.grade_id === ruleForm.dc_grade_id)?.grade_name,
+        dc_subject_name: Subject.find((item) => item.subject_id === ruleForm.dc_subject_id)?.subject_name,
+        dc_files: ruleForm.dc_files.map((item) => item.p_url).join(';'),
+        create_user_realname: user.user_realname,
+      })
+
+      request({
+        url: '/dyaw/ctfx/add',
+        data: {
+          dyaw_ctfx,
+        },
+      }).then(
+        res => {
+          if (res.code === '1') {
+            ElMessage.success('添加成功')
+            router.back()
+          } else {
+            ElMessage.error(res.msg)
+          }
+        }
+      ).catch(
+        err => {
+          ElMessage.error(err)
+        }
+      ).finally(
+        () => {
+          submit_loading.value = false
+        }
+      )
     } else {
-      console.log('error submit!', fields)
+      console.log('error submit!')
     }
   })
 }
@@ -133,22 +125,25 @@ function translateString(str: string) {
         <el-input v-model="ruleForm.dc_title" maxlength="20" show-word-limit />
       </el-form-item>
       <el-form-item label="年级" prop="dc_grade_id">
-        <el-select v-model="ruleForm.dc_grade_id">
-          <el-option label="Zone one" value="shanghai" />
-          <el-option label="Zone two" value="beijing" />
+        <el-select clearable v-model="ruleForm.dc_grade_id">
+          <el-option v-for="({ grade_id, grade_name }) in Grade" :key="grade_id" :label="grade_name" :value="grade_id" />
         </el-select>
       </el-form-item>
       <el-form-item label="科目" prop="dc_subject_id">
-        <el-select-v2 v-model="ruleForm.dc_subject_id" :options="options" />
+        <el-select clearable v-model="ruleForm.dc_subject_id">
+          <el-option v-for="({ subject_id, subject_name }) in Subject" :key="subject_id" :label="subject_name"
+            :value="subject_id" />
+        </el-select>
       </el-form-item>
       <el-form-item label="关键字" prop="dc_keyword">
         <el-input v-model="ruleForm.dc_keyword" maxlength="10" show-word-limit />
       </el-form-item>
       <el-form-item label="内容" prop="dc_content">
-        <el-input v-model="ruleForm.dc_content" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" />
+        <el-input v-model="ruleForm.dc_content" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" />
       </el-form-item>
       <el-form-item label="图片" prop="dc_files">
-        <el-upload v-model:file-list="ruleForm.dc_files" list-type="picture-card" :on-preview="handlePictureCardPreview">
+        <el-upload v-model:file-list="ruleForm.dc_files" list-type="picture-card" :on-preview="handlePictureCardPreview"
+          :action="UploadAction" name="filedata" :data="{ token, site: 'dyaw', }" :on-success="handleUploadSuccess">
           <el-icon>
             <Plus />
           </el-icon>
@@ -159,7 +154,7 @@ function translateString(str: string) {
         </el-dialog>
       </el-form-item>
       <el-form-item>
-        <el-button type="primary" @click="submitForm(ruleFormRef)">
+        <el-button type="primary" @click="submitForm(ruleFormRef)" :loading="submit_loading" >
           完成
         </el-button>
         <el-button @click="resetForm(ruleFormRef)">重置</el-button>

+ 51 - 0
src/pages/teacher/indey/detail.vue

@@ -0,0 +1,51 @@
+<script setup lang='ts'>
+const router = useRouter()
+import { Status } from '~/store/info'
+
+const props = defineProps<{
+  id: string
+}>()
+
+const detail = ref(null)
+await request({
+  url: '/dyaw/ctfx/detail',
+  data: {
+    dc_id: props.id
+  }
+}).then(
+  res => {
+    if (res.code === "1") {
+      detail.value = res.data.one_info
+    }
+  }
+)
+</script>
+
+<template>
+  <div class="w-1400px h-640px flex flex-col">
+    <div class="flex justify-between">
+      <div class="space-x-2 text-blue-700 flex items-center">
+        <i:cil:home />
+        <span>首页</span>
+        <span>/</span>
+        <span>错题分析</span>
+        <span>/</span>
+        <span>上传错题</span>
+        <span>/</span>
+        <span class="text-gray-600">详情</span>
+      </div>
+      <el-button type="primary" @click="router.back()">返回</el-button>
+    </div>
+
+
+    <cuoti-detail v-if="detail" :d="detail" />
+
+    <el-divider />
+    <div>
+      <span>审核结果:</span><span class="text-gray-600" >{{ Status[detail.dc_check] }}</span>
+    </div>
+    <div v-show="detail.dc_check === '2'">
+      <span>处理意见:</span><span class="text-gray-600">{{ detail.dc_check_desc }}</span>
+    </div>
+  </div>
+</template>

+ 195 - 0
src/pages/teacher/indey/edit.vue

@@ -0,0 +1,195 @@
+<script setup lang='ts'>
+import { useRouter, RouteLocationRaw } from 'vue-router'
+import type { FormInstance, FormRules } from 'element-plus'
+import { ref, reactive } from 'vue'
+import { Plus } from '@element-plus/icons-vue'
+import { Grade, Subject } from '~/store/info'
+import user from '~/store/user'
+const token = user.token
+
+const props = defineProps<{
+  id: string
+}>()
+
+import type { UploadProps, UploadUserFile } from 'element-plus'
+
+const router = useRouter()
+function routerPush(_route: RouteLocationRaw) {
+  router.push(_route)
+}
+
+const UploadAction = window.GLOBAL_CONFIG.oss + '/upload/main/file'
+function handleUploadSuccess(res: any, file: UploadUserFile, fileList: UploadUserFile[]) {
+  file.p_url = '/' + res.data.url
+}
+
+const ruleFormRef = ref<FormInstance>()
+const ruleForm = reactive({
+  dc_title: '',
+  dc_grade_id: '',
+  dc_subject_id: '',
+  dc_keyword: '',
+  dc_content: '',
+  dc_files: [],
+})
+
+await request({
+  url: '/dyaw/ctfx/detail',
+  data: {
+    dc_id: props.id
+  }
+}).then(
+  res => {
+    if (res.code === "1") {
+      const detail = res.data.one_info
+      Object.keys(ruleForm).forEach(key => {
+        if(key === 'dc_files'){
+          ruleForm[key] = detail[key].split(';').map((p_url: string) => {
+            return {
+              name: p_url,
+              url: window.GLOBAL_CONFIG.oss + p_url,
+              p_url: p_url,
+            }
+          })
+          return
+        }
+        ruleForm[key] = detail[key]
+      })
+    }
+  }
+)
+
+const rules = reactive<FormRules>({
+  dc_title: [{ required: true, message: '标题不能为空', trigger: 'blur', }],
+  dc_grade_id: [{ required: true, message: '年级不能为空', trigger: 'blur', }],
+  dc_subject_id: [{ required: true, message: '科目不能为空', trigger: 'blur', }],
+  dc_keyword: [{ required: true, message: '关键字不能为空', trigger: 'blur', }],
+})
+
+const submit_loading = ref(false)
+const submitForm = async (formEl: FormInstance | undefined) => {
+  console.log('ruleForm : ', ruleForm)
+  if (!formEl) return
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      submit_loading.value = true
+      console.log('submit!')
+      const dyaw_ctfx = Object.assign({}, ruleForm, {
+        dc_grade_name: Grade.find((item) => item.grade_id === ruleForm.dc_grade_id)?.grade_name,
+        dc_subject_name: Subject.find((item) => item.subject_id === ruleForm.dc_subject_id)?.subject_name,
+        dc_files: ruleForm.dc_files.map((item) => item.p_url).join(';'),
+        create_user_realname: user.user_realname,
+      })
+
+      request({
+        url: '/dyaw/ctfx/edit',
+        data: {
+          dc_id: props.id,
+          dyaw_ctfx,
+        },
+      }).then(
+        res => {
+          if (res.code === '1') {
+            ElMessage.success('编辑成功')
+            router.back()
+          } else {
+            ElMessage.error(res.msg)
+          }
+        }
+      ).catch(
+        err => {
+          ElMessage.error(err)
+        }
+      ).finally(
+        () => {
+          submit_loading.value = false
+        }
+      )
+    } else {
+      console.log('error submit!')
+    }
+  })
+}
+
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.resetFields()
+}
+
+const options = Array.from({ length: 10 }).map((_, idx) => ({
+  value: `${idx + 1}`,
+  label: `${idx + 1}`,
+}))
+
+const dialogImageUrl = ref('')
+const dialogVisible = ref(false)
+const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
+  dialogImageUrl.value = uploadFile.url!
+  dialogVisible.value = true
+}
+
+// TODO: 临时处理
+function translateString(str: string) {
+  return str.split('\n').map((item) => `<p>${item}</p>`).join('')
+}
+</script>
+
+<template>
+  <div class="w-1400px h-640px flex flex-col">
+    <div class="flex justify-between">
+      <div class="space-x-2 text-blue-700 flex items-center">
+        <i:cil:home />
+        <span>首页</span>
+        <span>/</span>
+        <span>错题分析</span>
+        <span>/</span>
+        <span>上传错题</span>
+        <span>/</span>
+        <span class="text-gray-600">新建</span>
+      </div>
+      <el-button type="primary" @click="router.back()">返回</el-button>
+    </div>
+
+
+    <el-form class="mt-8" ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="120px" size="large" status-icon>
+      <el-form-item label="标题" prop="dc_title">
+        <el-input v-model="ruleForm.dc_title" maxlength="20" show-word-limit />
+      </el-form-item>
+      <el-form-item label="年级" prop="dc_grade_id">
+        <el-select clearable v-model="ruleForm.dc_grade_id">
+          <el-option v-for="({ grade_id, grade_name }) in Grade" :key="grade_id" :label="grade_name" :value="grade_id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="科目" prop="dc_subject_id">
+        <el-select clearable v-model="ruleForm.dc_subject_id">
+          <el-option v-for="({ subject_id, subject_name }) in Subject" :key="subject_id" :label="subject_name"
+            :value="subject_id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="关键字" prop="dc_keyword">
+        <el-input v-model="ruleForm.dc_keyword" maxlength="10" show-word-limit />
+      </el-form-item>
+      <el-form-item label="内容" prop="dc_content">
+        <el-input v-model="ruleForm.dc_content" type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" />
+      </el-form-item>
+      <el-form-item label="图片" prop="dc_files">
+        <el-upload v-model:file-list="ruleForm.dc_files" list-type="picture-card" :on-preview="handlePictureCardPreview"
+          :action="UploadAction" name="filedata" :data="{ token, site: 'dyaw', }" :on-success="handleUploadSuccess">
+          <el-icon>
+            <Plus />
+          </el-icon>
+        </el-upload>
+
+        <el-dialog v-model="dialogVisible">
+          <img w-full :src="dialogImageUrl" alt="Preview Image" />
+        </el-dialog>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="submitForm(ruleFormRef)" :loading="submit_loading">
+          完成
+        </el-button>
+        <el-button @click="resetForm(ruleFormRef)">重置</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>

+ 1 - 1
src/router/routes/admin.ts

@@ -4,7 +4,7 @@ export default {
     {
       path: 'home',
       name: 'admin_home',
-      component: () => import('@/pages/admin/home.vue')
+      component: () => import('~/pages/admin/home.vue')
     },
   ]
 }

+ 2 - 2
src/router/routes/student.ts

@@ -4,12 +4,12 @@ export default {
     {
       path: 'index',
       name: 'student_index',
-      component: () => import('@/pages/student/index.vue')
+      component: () => import('~/pages/student/index.vue')
     },
     {
       path: 'detail/:id',
       name: 'student_detail',
-      component: () => import('@/pages/student/detail.vue')
+      component: () => import('~/pages/student/detail.vue')
     },
   ]
 }

+ 37 - 9
src/router/routes/teacher.ts

@@ -3,18 +3,46 @@ export default {
   children: [
     {
       path: 'index',
-      name: 'teacher_index',
-      component: () => import('@/pages/teacher/index.vue')
+      children: [
+        {
+          path: '',
+          name: 'teacher_index',
+          component: () => import('~/pages/teacher/index.vue')
+        },
+        {
+          path: 'detail/:id',
+          name: 'teacher_index_detail',
+          component: () => import('~/pages/teacher/index/detail.vue'),
+          props: true
+        },
+      ]
     },
     {
       path: 'indey',
-      name: 'teacher_indey',
-      component: () => import('@/pages/teacher/indey.vue')
-    },
-    {
-      path: 'create',
-      name: 'teacher_create',
-      component: () => import('@/pages/teacher/create.vue')
+      children: [
+        {
+          path: '',
+          name: 'teacher_indey',
+          component: () => import('~/pages/teacher/indey.vue')
+        },
+        {
+          path: 'create',
+          name: 'teacher_indey_create',
+          component: () => import('~/pages/teacher/indey/create.vue')
+        },
+        {
+          path: 'edit/:id',
+          name: 'teacher_indey_edit',
+          component: () => import('~/pages/teacher/indey/edit.vue'),
+          props: true
+        },
+        {
+          path: 'detail/:id',
+          name: 'teacher_indey_detail',
+          component: () => import('~/pages/teacher/indey/detail.vue'),
+          props: true
+        },
+      ]
     },
   ]
 }

+ 99 - 0
src/store/info.ts

@@ -0,0 +1,99 @@
+export const Grade: {
+  grade_id: string;
+  grade_name: string;
+}[] = [
+    {
+      "grade_id": "1",
+      "grade_name": "一年级"
+    },
+    {
+      "grade_id": "2",
+      "grade_name": "二年级"
+    },
+    {
+      "grade_id": "3",
+      "grade_name": "三年级"
+    },
+    {
+      "grade_id": "4",
+      "grade_name": "四年级"
+    },
+    {
+      "grade_id": "5",
+      "grade_name": "五年级"
+    },
+    {
+      "grade_id": "6",
+      "grade_name": "六年级"
+    },
+    {
+      "grade_id": "7",
+      "grade_name": "初一"
+    },
+    {
+      "grade_id": "8",
+      "grade_name": "初二"
+    },
+    {
+      "grade_id": "9",
+      "grade_name": "初三"
+    },
+    {
+      "grade_id": "11",
+      "grade_name": "高一"
+    },
+    {
+      "grade_id": "12",
+      "grade_name": "高二"
+    },
+    {
+      "grade_id": "13",
+      "grade_name": "高三"
+    }
+  ]
+
+export const Subject: {
+  subject_id: string;
+  subject_name: string;
+}[] = [
+    {
+      "subject_id": "1",
+      "subject_name": "语文"
+    },
+    {
+      "subject_id": "2",
+      "subject_name": "数学"
+    },
+    {
+      "subject_id": "3",
+      "subject_name": "外语"
+    },
+    {
+      "subject_id": "4",
+      "subject_name": "政治"
+    },
+    {
+      "subject_id": "5",
+      "subject_name": "物理"
+    },
+    {
+      "subject_id": "6",
+      "subject_name": "生物"
+    },
+    {
+      "subject_id": "7",
+      "subject_name": "地理"
+    },
+    {
+      "subject_id": "8",
+      "subject_name": "历史"
+    },
+    {
+      "subject_id": "9",
+      "subject_name": "化学"
+    }
+  ]
+
+export const Status = {
+  0: '待审核', 1: '通过', 2: '不通过'
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
src/store/user.store.ts


+ 25 - 0
tsconfig.json

@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "module": "ESNext",
+    "target": "es2016",
+    "lib": ["DOM", "ESNext"],
+    "strict": true,
+    "esModuleInterop": true,
+    "incremental": false,
+    "skipLibCheck": true,
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "noUnusedLocals": true,
+    "strictNullChecks": true,
+    "forceConsistentCasingInFileNames": true,
+    "types": [
+      "vite/client",
+      "vite-plugin-pages/client"
+    ],
+    "paths": {
+      "~/*": ["src/*"]
+    }
+  },
+  "exclude": ["dist", "node_modules"]
+}