Просмотр исходного кода

修复随访任务症状选择

张田田 8 месяцев назад
Родитель
Сommit
6cbcbb6896
1 измененных файлов с 650 добавлено и 650 удалено
  1. 650 650
      src/components/Follow.vue

+ 650 - 650
src/components/Follow.vue

@@ -1,650 +1,650 @@
-<script setup lang="ts">
-import type { TaskModel } from '@/model/follow.model';
-import { FillFollowContentMethod, UploadIFile, FollowContentMethod } from '@/request/api/follow.api';
-import { getDictionaryMethod } from '@/request/api/dictionary.api';
-import { useRequest } from 'alova/client';
-import { PlusOutlined } from '@ant-design/icons-vue';
-
-import { notification } from 'ant-design-vue';
-import type { UploadFile } from 'ant-design-vue/es/upload/interface';
-import { VxeUI } from 'vxe-pc-ui';
-type FormModel = Partial<TaskModel>;
-
-const props = defineProps<{ data: FormModel }>();
-
-const emits = defineEmits<{
-  submit: [data?: TaskModel];
-}>();
-const {
-  data: contentArr,
-  loading,
-  send: load,
-} = useRequest(() => FollowContentMethod(props.data), {
-  initialData: [],
-}).onSuccess(({ data }) => {
-  const index = data.findIndex((item) => item.id === props.data.id);
-  if (index > -1) {
-    changeTab(data[index], index);
-  }
-});
-const statusList = ref<string[]>([]);
-onBeforeMount(() => {
-  getDictionaryMethod('followup_syndrome_change').then((res) => {
-    statusList.value = res;
-  });
-});
-const activeKey = ref<number>();
-const activeIndex = ref<number>();
-const activeObj = ref<any>({ fillin: {}, symptomsData: [] });
-// 切换侧边栏任务
-const changeTab = (data: any, index: number) => {
-  activeKey.value = data.id;
-  activeObj.value = { ...data, fillin: { ...data.fillin } };
-  activeIndex.value = index;
-  activeObj.value.symptomsData = [];
-  downImageList.value = [];
-  const upImg = data.fillin?.upImg;
-  const downImg = data.fillin?.downImg;
-  const faceImg = data.fillin?.faceImg;
-  if (activeObj.value?.syndromeList && activeObj.value.syndromeList.length > 0) {
-    activeObj.value.syndromeList.forEach((syndrome) => {
-      activeObj.value.symptomsData.push({ name: syndrome });
-    });
-    activeObj.value.symptomsData.forEach((item) => {
-      item.child = statusList.value;
-      item.selectedValue = '';
-      item.selectedId = null;
-      item.id = data.id;
-    });
-  }
-  symptomsValue.value.parent = '';
-  upImgList.value = upImg
-    ? [
-        {
-          uid: '-1',
-          status: 'done',
-          url: upImg,
-          thumbUrl: upImg,
-          response: { url: upImg },
-        },
-      ]
-    : [];
-  downImageList.value = downImg
-    ? [
-        {
-          uid: '-1',
-          status: 'done',
-          url: downImg,
-          thumbUrl: downImg,
-          response: { url: downImg },
-        },
-      ]
-    : [];
-  faceImageList.value = faceImg
-    ? [
-        {
-          uid: '-1',
-          status: 'done',
-          url: faceImg,
-          thumbUrl: faceImg,
-          response: { url: faceImg },
-        },
-      ]
-    : [];
-  uploadProps.showRemoveIcon = data.progress === '1';
-};
-
-// 存储所有选择的症状
-const selectedSymptomsList = ref<{ name: string; value: string }[]>([]);
-// 存储症状
-const symptomsList = ref<{ name: string; type: string }[]>([]);
-// 症状选择的值
-const symptomsValue = ref({
-  parent: '',
-  child: '',
-});
-
-// 处理父级点击
-const handleParentClick = (name: string) => {
-  if (symptomsValue.value.parent === name) {
-    // 如果点击的是当前选中的父级,则清空选择
-    symptomsValue.value = {
-      parent: '',
-      child: '',
-    };
-  } else {
-    // 选择新的父级
-    symptomsValue.value = {
-      parent: name,
-      child: '',
-    };
-  }
-};
-
-// 处理子级选择变化
-const handleChildChange = (e: any) => {
-  const selectedValue = e.target.value;
-  const currentParent = symptomsValue.value.parent;
-  // 找到当前症状
-  const symptom = activeObj.value.symptomsData.find((item) => item.name === currentParent);
-
-  if (symptom) {
-    // 如果点击的是当前选中的值,则取消选择
-    if (symptom.selectedId === selectedValue) {
-      // 取消选择
-      symptom.selectedValue = '';
-      symptom.selectedId = null;
-
-      // 从已选择的症状列表中移除
-      const index = selectedSymptomsList.value.findIndex((item) => item.name === currentParent);
-      if (index > -1) {
-        selectedSymptomsList.value.splice(index, 1);
-      }
-    } else {
-      // 选择新的值
-      const child = symptom.child.find((item) => item.value === selectedValue);
-      if (child) {
-        symptom.selectedValue = selectedValue;
-        symptom.selectedId = selectedValue;
-
-        // 更新已选择的症状列表
-        const existingIndex = selectedSymptomsList.value.findIndex((item) => item.name === currentParent);
-        if (existingIndex > -1) {
-          selectedSymptomsList.value[existingIndex].value = selectedValue;
-        } else {
-          selectedSymptomsList.value.push({
-            name: currentParent,
-            value: selectedValue,
-          });
-        }
-      }
-    }
-  }
-  symptomsValue.value.parent = '';
-};
-
-// 是否出现新症状
-const selectSymptomsData = reactive([
-  { name: '有', id: 'Y' },
-  { name: '没有', id: 'N' },
-]);
-
-const uploadProps = reactive({ showRemoveIcon: true });
-const changeTag = (item: any) => {
-  activeObj.value.fillin.isHaveNewSyndrome = item.id;
-};
-
-const upImgList = ref<UploadFile[]>([]);
-const downImageList = ref<UploadFile[]>([]);
-const faceImageList = ref<UploadFile[]>([]);
-// 预览图片
-const handlePreview = async (file: UploadFile) => {
-  previewImg.value = file.response?.url ?? file.thumbUrl;
-  visible.value = true;
-};
-
-// 填写随访内容
-function subFollowContent() {
-  activeObj.value.fillin.upImg = upImgList.value[0]?.response?.url;
-  activeObj.value.fillin.downImg = downImageList.value[0]?.response?.url;
-  activeObj.value.fillin.faceImg = faceImageList.value[0]?.response?.url;
-  symptomsList.value = [];
-  activeObj.value.symptomsData.forEach((item) => {
-    symptomsList.value.push({ name: item.name, type: item.selectedValue });
-  });
-  activeObj.value.fillin.symptomsList = symptomsList.value;
-  FillFollowContentMethod(activeObj.value).then(() => {
-    notification.success({
-      message: '',
-      description: '提交成功!',
-    });
-    emits('submit');
-    load();
-  });
-}
-
-// 取消提交
-function cancelFollowContent() {
-  VxeUI.modal.close(`follow-modal`);
-}
-
-function customUpload(e: any) {
-  // uploadApi 你的二次封装上传接口
-  UploadIFile(e.file)
-    .then((res) => {
-      // 调用实例的成功方法通知组件该文件上传成功
-      e.onSuccess(res, e);
-    })
-    .catch((err) => {
-      // 调用实例的失败方法通知组件该文件上传失败
-      e.onError(err);
-    });
-}
-
-const visible = ref<boolean>(false);
-const setVisible = (value: boolean): void => {
-  visible.value = value;
-};
-const previewImg = ref<string>('');
-</script>
-
-<template>
-  <div>
-    <div class="flex font-bold">
-      <!--    左边-->
-      <div class="animated-vertical-tabs">
-        <div class="tab-list">
-          <div class="font-bold h-8 pt-3 mb-3 ml-2">{{ activeObj?.followupPlanName }}</div>
-          <div
-            style="font-size: 14px"
-            v-for="(content, index) in contentArr"
-            :key="content.id"
-            class="tab-item mb-3"
-            :class="{ active: activeKey === content.id }"
-            @click="changeTab(content, index)"
-          >
-            <div>{{ content.followupTaskName }}</div>
-            <span class="tab-label">{{ content.arrangeTime }}</span>
-            <div :class="content.progress == 1 ? 'text-red-600' : content.progress == 2 ? 'text-green-900' : content.progress == 3 ? 'text-blue-900' : ''">
-              {{ content.progress === '1' ? '未完成' : content.progress === '2' ? '已完成' : '未开始' }}
-            </div>
-          </div>
-        </div>
-      </div>
-      <!--    右边-->
-      <div :key="activeObj.id">
-        <div class="h-8 text-center">
-            {{ activeObj?.followupTaskName }}
-        </div>
-        <div class="mb-2 text-center">预定随访时间:{{ activeObj?.arrangeTime }}</div>
-        <div class="mb-2 ml-2">
-          您好,您于<span class="text-blue-600">【{{ activeObj?.medicalTime }}】</span>在我院<span class="text-blue-600">【{{ activeObj?.institutionName }}】</span>因为<span
-            class="text-blue-600"
-            >【{{ activeObj?.diagnosis }}】</span
-          >就诊。接下来我们将对您进行一个随访,请根据目前的实际情况回答。
-        </div>
-        <div
-          class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2"
-          v-if="(activeObj?.progress === '1' && activeObj?.symptomsData?.length > 0) || (activeObj.progress === '2' && activeObj?.fillin?.symptomsList?.length > 0)"
-        >
-          <div class="mb-3 border-b-0">
-            1、请问您的症状有没有<span class="text-red-600">好转</span>或者<span class="text-red-600">恶化</span>?请先点击症状,再选择好转还是恶化。(没有操作的症状默认没有变化)
-          </div>
-
-          <div class="ml-4" v-if="activeObj.progress === '1'">
-            <!-- 症状选择器 -->
-            <div class="symptom-container flex flex-wrap">
-              <div v-for="item in activeObj?.symptomsData" :key="item.name" class="symptom-item">
-                <div class="symptom-button" @click="activeObj.progress !== '0' ? handleParentClick(item.name) : ''" :class="{ 'disabled': activeObj.progress === '0' }">
-                  <span>{{ item.name }}</span>
-                  <span v-if="item.selectedValue" class="selected-value">: {{ item.selectedValue }}</span>
-                </div>
-                <div v-show="symptomsValue.parent === item.name" class="symptom-options">
-                  <a-radio-group :model-value="item.selectedId" @change="activeObj.progress !== '0' ? handleChildChange : ''" class="flex flex-wrap" :disabled="activeObj.progress === '0'">
-                    <a-radio :value="tag.value" v-for="tag in item.child" :key="tag.value" class="mr-4">
-                      {{ tag.label }}
-                    </a-radio>
-                  </a-radio-group>
-                </div>
-              </div>
-            </div>
-          </div>
-          <!-- 已经评估过 -->
-          <div v-else>
-            <div class="symptom-container flex flex-wrap">
-              <div v-for="item in activeObj?.fillin?.symptomsList" :key="item.name" class="symptom-item">
-                <div class="symptom-button">
-                  <span>{{ item.name }}</span>
-                  <span class="selected-value">: {{ item.type }}</span>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!--      第二个-->
-        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
-          <div class="mb-3">2、请问有没有出现<span class="text-red-600">新</span>的症状?</div>
-          <div class="mb-8 ml-4 flex">
-            <div v-for="symptoms in selectSymptomsData" :key="symptoms.name" class="mr-4" @click="activeObj?.progress === '1' ? changeTag(symptoms) : ''">
-              <div>
-                <div class="border-solid b-1 w-20 text-center" :class="[activeObj.fillin.isHaveNewSyndrome === symptoms.id ? 'bg-blue text-#fff' : '', activeObj.progress === '0' ? 'disabled' : '']">
-                  {{ symptoms.name }}
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-        <!--      第三个-->
-        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2" v-if="activeObj.fillin?.isHaveNewSyndrome === 'Y'">
-          <div class="mb-3">3、请描述新的症状</div>
-          <div class="mb-4 ml-4">
-            <a-input v-model:value="activeObj.fillin.newSyndrome" placeholder="请输入" :auto-size="{ minRows: 2, maxRows: 5 }" :disabled="activeObj.progress === '2' || activeObj.progress === '0'" />
-          </div>
-        </div>
-        <!--      第四个-->
-        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
-          <div class="mb-3">4、如果有其他情况,请留言</div>
-          <div class="mb-4 ml-4">
-            <a-input v-model:value="activeObj.fillin.otherDesc" placeholder="请输入" :auto-size="{ minRows: 2, maxRows: 5 }" :disabled="activeObj.progress === '2' || activeObj.progress === '0'" />
-          </div>
-        </div>
-        <!--      第五个-->
-        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
-          <div class="mb-3">5、为了医生更好地了解您的恢复情况,需要您上传舌面象照片</div>
-          <div class="mb-4 ml-3">
-            <!--  上传图片-->
-            <div class="flex">
-              <!--            舌面-->
-              <div class="flex flex-col items-center mr-4">
-                <a-upload
-                  :showUploadList="uploadProps"
-                  v-model:file-list="upImgList"
-                  list-type="picture-card"
-                  @preview="handlePreview"
-                  :maxCount="1"
-                  :customRequest="customUpload"
-                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
-                >
-                  <div v-if="upImgList.length < 1">
-                    <plus-outlined />
-                  </div>
-                </a-upload>
-                <div class="font-bold">舌面</div>
-              </div>
-              <!--            舌下-->
-              <div class="flex flex-col items-center mr-4">
-                <a-upload
-                  :showUploadList="uploadProps"
-                  v-model:file-list="downImageList"
-                  list-type="picture-card"
-                  @preview="handlePreview"
-                  :maxCount="1"
-                  :customRequest="customUpload"
-                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
-                >
-                  <div v-if="downImageList.length < 1">
-                    <plus-outlined />
-                  </div>
-                </a-upload>
-                <div class="font-bold">舌下</div>
-              </div>
-              <!--            面部-->
-              <div class="flex flex-col items-center mr-4">
-                <a-upload
-                  :showUploadList="uploadProps"
-                  v-model:file-list="faceImageList"
-                  list-type="picture-card"
-                  @preview="handlePreview"
-                  :maxCount="1"
-                  :customRequest="customUpload"
-                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
-                >
-                  <div v-if="faceImageList.length < 1">
-                    <plus-outlined />
-                  </div>
-                </a-upload>
-                <div class="font-bold">面部</div>
-              </div>
-              <!--              --------end-->
-            </div>
-            <a-image
-              :width="200"
-              :style="{ display: 'none' }"
-              :preview="{
-                visible,
-                onVisibleChange: setVisible,
-              }"
-              :src="previewImg"
-            />
-          </div>
-        </div>
-
-        <!--      -->
-        <div class="ml-2 mt-1">
-          感谢您的配合,为了更好地了解您的回复情况,我们将会在<span class="text-blue-600"> {{ activeObj?.arrangeTime }} </span
-          >再次对您进行随访,届时请点击随访链接参与,再次感谢您!
-        </div>
-      </div>
-    </div>
-    <div class="flex items-center justify-center mt-6 mb-6">
-      <a-button size="small" class="mr-4" @click="cancelFollowContent">取消</a-button>
-      <a-button type="primary" size="small" @click="subFollowContent" v-show="activeObj.progress === '1'">提交 </a-button>
-    </div>
-  </div>
-</template>
-<style scoped lang="scss">
-.ant-upload-select-picture-card i {
-  font-size: 32px;
-  color: #999;
-}
-
-.ant-upload-select-picture-card .ant-upload-text {
-  margin-top: 8px;
-  color: #666;
-}
-
-.mesh-grid {
-  border-collapse: collapse;
-}
-
-.mesh-grid td {
-  border: 1px solid black;
-  width: 100px;
-  padding: 20px 20px;
-  text-align: center;
-}
-
-.animated-vertical-tabs {
-  display: flex;
-  height: 730px;
-  width: 17%;
-  overflow: auto;
-}
-
-.tab-list {
-  border-right: 1px solid #f0f0f0;
-}
-
-.tab-item {
-  position: relative;
-  padding: 10px;
-  cursor: pointer;
-  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-  font-weight: bold;
-}
-
-.tab-item:hover {
-  background-color: rgba(24, 144, 255, 0.06);
-}
-
-.tab-item.active {
-  background: lightgray;
-}
-
-.tab-indicator {
-  position: absolute;
-  top: 0;
-  right: -1px;
-  width: 2px;
-  height: 100%;
-  background-color: #1890ff;
-  transform: scaleY(0);
-  transform-origin: center top;
-  transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-}
-
-.tab-item.active .tab-indicator {
-  transform: scaleY(1);
-}
-
-.tab-content {
-  flex: 1;
-  padding: 0 24px;
-  overflow: auto;
-}
-
-.fade-enter-active,
-.fade-leave-active {
-  transition: opacity 0.3s ease;
-}
-
-.fade-enter-from,
-.fade-leave-to {
-  opacity: 0;
-}
-
-// 症状选择器样式
-.symptom-container {
-  .symptom-item {
-    position: relative;
-    margin-right: 16px;
-    margin-bottom: 16px;
-
-    .symptom-button {
-      min-width: 100px;
-      padding: 8px 16px;
-      border: 1px solid #d9d9d9;
-      border-radius: 4px;
-      text-align: center;
-      cursor: pointer;
-      transition: all 0.3s;
-      background: #fff;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-
-      &:hover {
-        border-color: #40a9ff;
-        color: #40a9ff;
-      }
-
-      &.active {
-        border-color: #1890ff;
-        color: #1890ff;
-        background: rgba(24, 144, 255, 0.1);
-      }
-
-      &.has-value {
-        border-color: #52c41a;
-        color: #52c41a;
-      }
-
-      .selected-value {
-        margin-left: 4px;
-        font-weight: 500;
-      }
-    }
-
-    .symptom-options {
-      position: absolute;
-      top: 100%;
-      left: 0;
-      z-index: 1;
-      margin-top: 8px;
-      padding: 8px;
-      background: #fff;
-      border: 1px solid #d9d9d9;
-      border-radius: 4px;
-      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
-      animation: fadeIn 0.3s;
-
-      .ant-radio-group {
-        display: flex;
-        flex-direction: column;
-        gap: 8px;
-      }
-
-      .ant-radio-wrapper {
-        padding: 4px 8px;
-        border-radius: 4px;
-        transition: all 0.3s;
-
-        &:hover {
-          background: rgba(24, 144, 255, 0.1);
-        }
-      }
-    }
-  }
-}
-
-// 已选择症状标签样式
-.selected-symptoms {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 8px;
-  margin-bottom: 16px;
-
-  .ant-tag {
-    margin: 0;
-    padding: 4px 8px;
-    border-radius: 4px;
-    transition: all 0.3s;
-
-    &:hover {
-      background: rgba(24, 144, 255, 0.1);
-    }
-  }
-}
-
-// 动画
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-    transform: translateY(-10px);
-  }
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-// 整体布局优化
-.border-1 {
-  margin-bottom: 16px;
-  padding: 16px;
-  border-radius: 8px;
-  background: #fff;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.mb-8 {
-  margin-bottom: 32px;
-}
-
-.ml-4 {
-  margin-left: 16px;
-}
-
-.text-blue-600 {
-  color: #1890ff;
-}
-
-.text-red-600 {
-  color: #ff4d4f;
-}
-
-.font-bold {
-  font-weight: 600;
-}
-
-// 禁用状态样式
-.disabled {
-  opacity: 0.5;
-  cursor: not-allowed !important;
-  pointer-events: none;
-}
-
-.symptom-button.disabled {
-  background-color: #f5f5f5;
-  color: #999;
-  border-color: #d9d9d9;
-  cursor: not-allowed;
-}
-
-.symptom-button.disabled:hover {
-  border-color: #d9d9d9;
-  color: #999;
-  background-color: #f5f5f5;
-}
-</style>
+<script setup lang="ts">
+import type { TaskModel } from '@/model/follow.model';
+import { FillFollowContentMethod, UploadIFile, FollowContentMethod } from '@/request/api/follow.api';
+import { getDictionaryMethod } from '@/request/api/dictionary.api';
+import { useRequest } from 'alova/client';
+import { PlusOutlined } from '@ant-design/icons-vue';
+
+import { notification } from 'ant-design-vue';
+import type { UploadFile } from 'ant-design-vue/es/upload/interface';
+import { VxeUI } from 'vxe-pc-ui';
+type FormModel = Partial<TaskModel>;
+
+const props = defineProps<{ data: FormModel }>();
+
+const emits = defineEmits<{
+  submit: [data?: TaskModel];
+}>();
+const {
+  data: contentArr,
+  loading,
+  send: load,
+} = useRequest(() => FollowContentMethod(props.data), {
+  initialData: [],
+}).onSuccess(({ data }) => {
+  const index = data.findIndex((item) => item.id === props.data.id);
+  if (index > -1) {
+    changeTab(data[index], index);
+  }
+});
+const statusList = ref<string[]>([]);
+onBeforeMount(() => {
+  getDictionaryMethod('followup_syndrome_change').then((res) => {
+    statusList.value = res;
+  });
+});
+const activeKey = ref<number>();
+const activeIndex = ref<number>();
+const activeObj = ref<any>({ fillin: {}, symptomsData: [] });
+// 切换侧边栏任务
+const changeTab = (data: any, index: number) => {
+  activeKey.value = data.id;
+  activeObj.value = { ...data, fillin: { ...data.fillin } };
+  activeIndex.value = index;
+  activeObj.value.symptomsData = [];
+  downImageList.value = [];
+  const upImg = data.fillin?.upImg;
+  const downImg = data.fillin?.downImg;
+  const faceImg = data.fillin?.faceImg;
+  if (activeObj.value?.syndromeList && activeObj.value.syndromeList.length > 0) {
+    activeObj.value.syndromeList.forEach((syndrome) => {
+      activeObj.value.symptomsData.push({ name: syndrome });
+    });
+    activeObj.value.symptomsData.forEach((item) => {
+      item.child = statusList.value;
+      item.selectedValue = '';
+      item.selectedId = null;
+      item.id = data.id;
+    });
+  }
+  symptomsValue.value.parent = '';
+  upImgList.value = upImg
+    ? [
+        {
+          uid: '-1',
+          status: 'done',
+          url: upImg,
+          thumbUrl: upImg,
+          response: { url: upImg },
+        },
+      ]
+    : [];
+  downImageList.value = downImg
+    ? [
+        {
+          uid: '-1',
+          status: 'done',
+          url: downImg,
+          thumbUrl: downImg,
+          response: { url: downImg },
+        },
+      ]
+    : [];
+  faceImageList.value = faceImg
+    ? [
+        {
+          uid: '-1',
+          status: 'done',
+          url: faceImg,
+          thumbUrl: faceImg,
+          response: { url: faceImg },
+        },
+      ]
+    : [];
+  uploadProps.showRemoveIcon = data.progress === '1';
+};
+
+// 存储所有选择的症状
+const selectedSymptomsList = ref<{ name: string; value: string }[]>([]);
+// 存储症状
+const symptomsList = ref<{ name: string; type: string }[]>([]);
+// 症状选择的值
+const symptomsValue = ref({
+  parent: '',
+  child: '',
+});
+
+// 处理父级点击
+const handleParentClick = (name: string) => {
+  if (symptomsValue.value.parent === name) {
+    // 如果点击的是当前选中的父级,则清空选择
+    symptomsValue.value = {
+      parent: '',
+      child: '',
+    };
+  } else {
+    // 选择新的父级
+    symptomsValue.value = {
+      parent: name,
+      child: '',
+    };
+  }
+};
+
+// 处理子级选择变化
+const handleChildChange = (e: any) => {
+  const selectedValue = e.target.value;
+  const currentParent = symptomsValue.value.parent;
+  // 找到当前症状
+  const symptom = activeObj.value.symptomsData.find((item) => item.name === currentParent);
+
+  if (symptom) {
+    // 如果点击的是当前选中的值,则取消选择
+    if (symptom.selectedId === selectedValue) {
+      // 取消选择
+      symptom.selectedValue = '';
+      symptom.selectedId = null;
+
+      // 从已选择的症状列表中移除
+      const index = selectedSymptomsList.value.findIndex((item) => item.name === currentParent);
+      if (index > -1) {
+        selectedSymptomsList.value.splice(index, 1);
+      }
+    } else {
+      // 选择新的值
+      const child = symptom.child.find((item) => item.value === selectedValue);
+      if (child) {
+        symptom.selectedValue = selectedValue;
+        symptom.selectedId = selectedValue;
+
+        // 更新已选择的症状列表
+        const existingIndex = selectedSymptomsList.value.findIndex((item) => item.name === currentParent);
+        if (existingIndex > -1) {
+          selectedSymptomsList.value[existingIndex].value = selectedValue;
+        } else {
+          selectedSymptomsList.value.push({
+            name: currentParent,
+            value: selectedValue,
+          });
+        }
+      }
+    }
+  }
+  symptomsValue.value.parent = '';
+};
+
+// 是否出现新症状
+const selectSymptomsData = reactive([
+  { name: '有', id: 'Y' },
+  { name: '没有', id: 'N' },
+]);
+
+const uploadProps = reactive({ showRemoveIcon: true });
+const changeTag = (item: any) => {
+  activeObj.value.fillin.isHaveNewSyndrome = item.id;
+};
+
+const upImgList = ref<UploadFile[]>([]);
+const downImageList = ref<UploadFile[]>([]);
+const faceImageList = ref<UploadFile[]>([]);
+// 预览图片
+const handlePreview = async (file: UploadFile) => {
+  previewImg.value = file.response?.url ?? file.thumbUrl;
+  visible.value = true;
+};
+
+// 填写随访内容
+function subFollowContent() {
+  activeObj.value.fillin.upImg = upImgList.value[0]?.response?.url;
+  activeObj.value.fillin.downImg = downImageList.value[0]?.response?.url;
+  activeObj.value.fillin.faceImg = faceImageList.value[0]?.response?.url;
+  symptomsList.value = [];
+  activeObj.value.symptomsData.forEach((item) => {
+    symptomsList.value.push({ name: item.name, type: item.selectedValue });
+  });
+  activeObj.value.fillin.symptomsList = symptomsList.value;
+  FillFollowContentMethod(activeObj.value).then(() => {
+    notification.success({
+      message: '',
+      description: '提交成功!',
+    });
+    emits('submit');
+    load();
+  });
+}
+
+// 取消提交
+function cancelFollowContent() {
+  VxeUI.modal.close(`follow-modal`);
+}
+
+function customUpload(e: any) {
+  // uploadApi 你的二次封装上传接口
+  UploadIFile(e.file)
+    .then((res) => {
+      // 调用实例的成功方法通知组件该文件上传成功
+      e.onSuccess(res, e);
+    })
+    .catch((err) => {
+      // 调用实例的失败方法通知组件该文件上传失败
+      e.onError(err);
+    });
+}
+
+const visible = ref<boolean>(false);
+const setVisible = (value: boolean): void => {
+  visible.value = value;
+};
+const previewImg = ref<string>('');
+</script>
+
+<template>
+  <div>
+    <div class="flex font-bold">
+      <!--    左边-->
+      <div class="animated-vertical-tabs">
+        <div class="tab-list">
+          <div class="font-bold h-8 pt-3 mb-3 ml-2">{{ activeObj?.followupPlanName }}</div>
+          <div
+            style="font-size: 14px"
+            v-for="(content, index) in contentArr"
+            :key="content.id"
+            class="tab-item mb-3"
+            :class="{ active: activeKey === content.id }"
+            @click="changeTab(content, index)"
+          >
+            <div>{{ content.followupTaskName }}</div>
+            <span class="tab-label">{{ content.arrangeTime }}</span>
+            <div :class="content.progress == 1 ? 'text-red-600' : content.progress == 2 ? 'text-green-900' : content.progress == 3 ? 'text-blue-900' : ''">
+              {{ content.progress === '1' ? '未完成' : content.progress === '2' ? '已完成' : '未开始' }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <!--    右边-->
+      <div :key="activeObj.id">
+        <div class="h-8 text-center">
+            {{ activeObj?.followupTaskName }}
+        </div>
+        <div class="mb-2 text-center">预定随访时间:{{ activeObj?.arrangeTime }}</div>
+        <div class="mb-2 ml-2">
+          您好,您于<span class="text-blue-600">【{{ activeObj?.medicalTime }}】</span>在我院<span class="text-blue-600">【{{ activeObj?.institutionName }}】</span>因为<span
+            class="text-blue-600"
+            >【{{ activeObj?.diagnosis }}】</span
+          >就诊。接下来我们将对您进行一个随访,请根据目前的实际情况回答。
+        </div>
+        <div
+          class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2"
+          v-if="(activeObj?.progress === '1' && activeObj?.symptomsData?.length > 0) || (activeObj.progress === '2' && activeObj?.fillin?.symptomsList?.length > 0)"
+        >
+          <div class="mb-3 border-b-0">
+            1、请问您的症状有没有<span class="text-red-600">好转</span>或者<span class="text-red-600">恶化</span>?请先点击症状,再选择好转还是恶化。(没有操作的症状默认没有变化)
+          </div>
+
+          <div class="ml-4" v-if="activeObj.progress === '1'">
+            <!-- 症状选择器 -->
+            <div class="symptom-container flex flex-wrap">
+              <div v-for="item in activeObj?.symptomsData" :key="item.name" class="symptom-item">
+                <div class="symptom-button" @click="activeObj.progress !== '0' ? handleParentClick(item.name) : ''" :class="{ 'disabled': activeObj.progress === '0' }">
+                  <span>{{ item.name }}</span>
+                  <span v-if="item.selectedValue" class="selected-value">: {{ item.selectedValue }}</span>
+                </div>
+                <div v-show="symptomsValue.parent === item.name" class="symptom-options">
+                  <a-radio-group :model-value="item.selectedId" @change="activeObj.progress !== '0' && handleChildChange($event)" class="flex flex-wrap" :disabled="activeObj.progress === '0'">
+                    <a-radio :value="tag.value" v-for="tag in item.child" :key="tag.value" class="mr-4">
+                      {{ tag.label }}
+                    </a-radio>
+                  </a-radio-group>
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- 已经评估过 -->
+          <div v-else>
+            <div class="symptom-container flex flex-wrap">
+              <div v-for="item in activeObj?.fillin?.symptomsList" :key="item.name" class="symptom-item">
+                <div class="symptom-button">
+                  <span>{{ item.name }}</span>
+                  <span class="selected-value">: {{ item.type }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <!--      第二个-->
+        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
+          <div class="mb-3">2、请问有没有出现<span class="text-red-600">新</span>的症状?</div>
+          <div class="mb-8 ml-4 flex">
+            <div v-for="symptoms in selectSymptomsData" :key="symptoms.name" class="mr-4" @click="activeObj?.progress === '1' ? changeTag(symptoms) : ''">
+              <div>
+                <div class="border-solid b-1 w-20 text-center" :class="[activeObj.fillin.isHaveNewSyndrome === symptoms.id ? 'bg-blue text-#fff' : '', activeObj.progress === '0' ? 'disabled' : '']">
+                  {{ symptoms.name }}
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!--      第三个-->
+        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2" v-if="activeObj.fillin?.isHaveNewSyndrome === 'Y'">
+          <div class="mb-3">3、请描述新的症状</div>
+          <div class="mb-4 ml-4">
+            <a-input v-model:value="activeObj.fillin.newSyndrome" placeholder="请输入" :auto-size="{ minRows: 2, maxRows: 5 }" :disabled="activeObj.progress === '2' || activeObj.progress === '0'" />
+          </div>
+        </div>
+        <!--      第四个-->
+        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
+          <div class="mb-3">4、如果有其他情况,请留言</div>
+          <div class="mb-4 ml-4">
+            <a-input v-model:value="activeObj.fillin.otherDesc" placeholder="请输入" :auto-size="{ minRows: 2, maxRows: 5 }" :disabled="activeObj.progress === '2' || activeObj.progress === '0'" />
+          </div>
+        </div>
+        <!--      第五个-->
+        <div class="border-1 border-solid border-gray:50 pl-2 pd-10 ml-2">
+          <div class="mb-3">5、为了医生更好地了解您的恢复情况,需要您上传舌面象照片</div>
+          <div class="mb-4 ml-3">
+            <!--  上传图片-->
+            <div class="flex">
+              <!--            舌面-->
+              <div class="flex flex-col items-center mr-4">
+                <a-upload
+                  :showUploadList="uploadProps"
+                  v-model:file-list="upImgList"
+                  list-type="picture-card"
+                  @preview="handlePreview"
+                  :maxCount="1"
+                  :customRequest="customUpload"
+                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
+                >
+                  <div v-if="upImgList.length < 1">
+                    <plus-outlined />
+                  </div>
+                </a-upload>
+                <div class="font-bold">舌面</div>
+              </div>
+              <!--            舌下-->
+              <div class="flex flex-col items-center mr-4">
+                <a-upload
+                  :showUploadList="uploadProps"
+                  v-model:file-list="downImageList"
+                  list-type="picture-card"
+                  @preview="handlePreview"
+                  :maxCount="1"
+                  :customRequest="customUpload"
+                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
+                >
+                  <div v-if="downImageList.length < 1">
+                    <plus-outlined />
+                  </div>
+                </a-upload>
+                <div class="font-bold">舌下</div>
+              </div>
+              <!--            面部-->
+              <div class="flex flex-col items-center mr-4">
+                <a-upload
+                  :showUploadList="uploadProps"
+                  v-model:file-list="faceImageList"
+                  list-type="picture-card"
+                  @preview="handlePreview"
+                  :maxCount="1"
+                  :customRequest="customUpload"
+                  :disabled="activeObj.progress === '2' || activeObj.progress === '0'"
+                >
+                  <div v-if="faceImageList.length < 1">
+                    <plus-outlined />
+                  </div>
+                </a-upload>
+                <div class="font-bold">面部</div>
+              </div>
+              <!--              --------end-->
+            </div>
+            <a-image
+              :width="200"
+              :style="{ display: 'none' }"
+              :preview="{
+                visible,
+                onVisibleChange: setVisible,
+              }"
+              :src="previewImg"
+            />
+          </div>
+        </div>
+
+        <!--      -->
+        <div class="ml-2 mt-1">
+          感谢您的配合,为了更好地了解您的回复情况,我们将会在<span class="text-blue-600"> {{ activeObj?.arrangeTime }} </span
+          >再次对您进行随访,届时请点击随访链接参与,再次感谢您!
+        </div>
+      </div>
+    </div>
+    <div class="flex items-center justify-center mt-6 mb-6">
+      <a-button size="small" class="mr-4" @click="cancelFollowContent">取消</a-button>
+      <a-button type="primary" size="small" @click="subFollowContent" v-show="activeObj.progress === '1'">提交 </a-button>
+    </div>
+  </div>
+</template>
+<style scoped lang="scss">
+.ant-upload-select-picture-card i {
+  font-size: 32px;
+  color: #999;
+}
+
+.ant-upload-select-picture-card .ant-upload-text {
+  margin-top: 8px;
+  color: #666;
+}
+
+.mesh-grid {
+  border-collapse: collapse;
+}
+
+.mesh-grid td {
+  border: 1px solid black;
+  width: 100px;
+  padding: 20px 20px;
+  text-align: center;
+}
+
+.animated-vertical-tabs {
+  display: flex;
+  height: 730px;
+  width: 17%;
+  overflow: auto;
+}
+
+.tab-list {
+  border-right: 1px solid #f0f0f0;
+}
+
+.tab-item {
+  position: relative;
+  padding: 10px;
+  cursor: pointer;
+  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  font-weight: bold;
+}
+
+.tab-item:hover {
+  background-color: rgba(24, 144, 255, 0.06);
+}
+
+.tab-item.active {
+  background: lightgray;
+}
+
+.tab-indicator {
+  position: absolute;
+  top: 0;
+  right: -1px;
+  width: 2px;
+  height: 100%;
+  background-color: #1890ff;
+  transform: scaleY(0);
+  transform-origin: center top;
+  transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+.tab-item.active .tab-indicator {
+  transform: scaleY(1);
+}
+
+.tab-content {
+  flex: 1;
+  padding: 0 24px;
+  overflow: auto;
+}
+
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.3s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+  opacity: 0;
+}
+
+// 症状选择器样式
+.symptom-container {
+  .symptom-item {
+    position: relative;
+    margin-right: 16px;
+    margin-bottom: 16px;
+
+    .symptom-button {
+      min-width: 100px;
+      padding: 8px 16px;
+      border: 1px solid #d9d9d9;
+      border-radius: 4px;
+      text-align: center;
+      cursor: pointer;
+      transition: all 0.3s;
+      background: #fff;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      &:hover {
+        border-color: #40a9ff;
+        color: #40a9ff;
+      }
+
+      &.active {
+        border-color: #1890ff;
+        color: #1890ff;
+        background: rgba(24, 144, 255, 0.1);
+      }
+
+      &.has-value {
+        border-color: #52c41a;
+        color: #52c41a;
+      }
+
+      .selected-value {
+        margin-left: 4px;
+        font-weight: 500;
+      }
+    }
+
+    .symptom-options {
+      position: absolute;
+      top: 100%;
+      left: 0;
+      z-index: 1;
+      margin-top: 8px;
+      padding: 8px;
+      background: #fff;
+      border: 1px solid #d9d9d9;
+      border-radius: 4px;
+      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+      animation: fadeIn 0.3s;
+
+      .ant-radio-group {
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+      }
+
+      .ant-radio-wrapper {
+        padding: 4px 8px;
+        border-radius: 4px;
+        transition: all 0.3s;
+
+        &:hover {
+          background: rgba(24, 144, 255, 0.1);
+        }
+      }
+    }
+  }
+}
+
+// 已选择症状标签样式
+.selected-symptoms {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  margin-bottom: 16px;
+
+  .ant-tag {
+    margin: 0;
+    padding: 4px 8px;
+    border-radius: 4px;
+    transition: all 0.3s;
+
+    &:hover {
+      background: rgba(24, 144, 255, 0.1);
+    }
+  }
+}
+
+// 动画
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-10px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+// 整体布局优化
+.border-1 {
+  margin-bottom: 16px;
+  padding: 16px;
+  border-radius: 8px;
+  background: #fff;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.mb-8 {
+  margin-bottom: 32px;
+}
+
+.ml-4 {
+  margin-left: 16px;
+}
+
+.text-blue-600 {
+  color: #1890ff;
+}
+
+.text-red-600 {
+  color: #ff4d4f;
+}
+
+.font-bold {
+  font-weight: 600;
+}
+
+// 禁用状态样式
+.disabled {
+  opacity: 0.5;
+  cursor: not-allowed !important;
+  pointer-events: none;
+}
+
+.symptom-button.disabled {
+  background-color: #f5f5f5;
+  color: #999;
+  border-color: #d9d9d9;
+  cursor: not-allowed;
+}
+
+.symptom-button.disabled:hover {
+  border-color: #d9d9d9;
+  color: #999;
+  background-color: #f5f5f5;
+}
+</style>