“hanlingqiang пре 3 месеци
родитељ
комит
82cfd52dfb

+ 3 - 1
README.md

@@ -1,4 +1,6 @@
 # asd-mobild
 ## 打包命令
-### npm run build zhulao
+### npm run build zhulao 正式环境
+### npm run build weibao 正式环境
+### npm run serve 运行后为 http://localhost:12004/zhulao/ 想访问weibao目录就手动切换到 http://localhost:12004/weibao/
 

+ 52 - 0
src/views/weibao/api/index.js

@@ -314,4 +314,56 @@ export function createChildBaseInfo(data) {
       method: 'post',
       data
     })
+}
+
+// 预警风险描述 
+export function warningReasons(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningReasons',
+      method: 'post',
+      data
+    })
+}
+
+// 预警风险保存 
+export function warningSave(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningSave',
+      method: 'post',
+      data
+    })
+}
+
+// 预警处置记录保存
+export function warningRecordSave(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningRecordSave',
+      method: 'post',
+      data
+    })
+}
+
+// 预警处置记录列表
+export function warningRecordList(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningRecordList',
+      method: 'post',
+      data
+    })
+}
+// 预警处置记录详情
+export function warningRecordDetail(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningRecordDetail',
+      method: 'post',
+      data
+    })
+}
+// 预警详情 
+export function warningDetail(data) {
+    return request({
+      url: '/pmadmin/app/mini/warningDetail',
+      method: 'post',
+      data
+    })
 }

+ 182 - 0
src/views/weibao/components/warningSheet.vue

@@ -0,0 +1,182 @@
+<template>
+  <van-action-sheet
+    v-model="show"
+    :close-on-click-action="false"
+    :close-on-click-outside="false"
+  >
+    <!-- 头部 -->
+    <div class="sheet-header">
+      <div class="title">请选择风险描述</div>
+      <van-button
+        size="small"
+        type="primary"
+        @click="confirmSelect"
+        :disabled="!currentValue"
+      >
+        确定
+      </van-button>
+    </div>
+
+    <!-- 红色预警 -->
+    <div class="group-title red">红色预警</div>
+    <van-cell
+      v-for="item in redList"
+      :key="item.value"
+      :title="item.label"
+      @click="selectItem(item)"
+      :class="{ active: currentValue == item.value }"
+    />
+
+    <!-- 橙色预警 -->
+    <div class="group-title orange">橙色预警</div>
+    <van-cell
+      v-for="item in orangeList"
+      :key="item.value"
+      :title="item.label"
+      @click="selectItem(item)"
+      :class="{ active: currentValue == item.value }"
+    />
+
+    <!-- 黄色预警 -->
+    <div class="group-title yellow">黄色预警</div>
+    <van-cell
+      v-for="item in yellowList"
+      :key="item.value"
+      :title="item.label"
+      @click="selectItem(item)"
+      :class="{ active: currentValue == item.value }"
+    />
+
+    <!-- 蓝色预警 -->
+    <div class="group-title blue">蓝色预警</div>
+    <van-cell
+      v-for="item in blueList"
+      :key="item.value"
+      :title="item.label"
+      @click="selectItem(item)"
+      :class="{ active: currentValue == item.value }"
+    />
+  </van-action-sheet>
+</template>
+
+<script>
+// Vant 官方弹窗 —— 你项目一定支持
+import { Dialog } from "vant";
+
+export default {
+  name: "WarningSheet",
+  props: {
+    value: { type: Boolean, default: false },
+    list: { type: Array, default: () => [] },
+    selectedValue: { type: [String, Number], default: "" },
+  },
+  data() {
+    return {
+      currentValue: this.selectedValue,
+      currentItem: null,
+    };
+  },
+  watch: {
+    selectedValue: {
+      immediate: true,
+      handler(val) {
+        this.currentValue = val;
+      },
+    },
+  },
+  computed: {
+    show: {
+      get() {
+        return this.value;
+      },
+      set(v) {
+        this.$emit("input", v);
+      },
+    },
+    redList() {
+      return this.list.filter((i) => i.label.includes("(红)"));
+    },
+    orangeList() {
+      return this.list.filter((i) => i.label.includes("(橙)"));
+    },
+    yellowList() {
+      return this.list.filter((i) => i.label.includes("(黄)"));
+    },
+    blueList() {
+      return this.list.filter((i) => i.label.includes("(蓝)"));
+    },
+  },
+  methods: {
+    selectItem(item) {
+      this.currentValue = item.value;
+      this.currentItem = item;
+    },
+
+    // ========== 加了 VANT 确认弹窗 ==========
+    confirmSelect() {
+      if (!this.currentItem) return;
+      Dialog.confirm({
+        title: "是否确认选择?",
+        message: `提交后不可修改,请确认!`,
+      })
+        .then(() => {
+          // 点击确认
+          let levelText = "";
+          if (this.currentItem.label.includes("(红)")) levelText = "红色预警";
+          if (this.currentItem.label.includes("(橙)")) levelText = "橙色预警";
+          if (this.currentItem.label.includes("(黄)")) levelText = "黄色预警";
+          if (this.currentItem.label.includes("(蓝)")) levelText = "蓝色预警";
+
+          this.$emit("confirm", {
+            item: this.currentItem,
+            levelText: levelText,
+          });
+          this.show = false;
+        })
+        .catch(() => {
+          // 取消,不执行
+        });
+    },
+  },
+};
+</script>
+
+<style scoped>
+.sheet-header {
+  display: flex;
+  justify-content: space-between;
+  padding: 20px;
+  border-bottom: 1px solid #eee;
+}
+.title {
+  font-size: 28px;
+  font-weight: bold;
+}
+.group-title {
+  padding: 15px 20px;
+  font-size: 26px;
+  background: #f5f5f5;
+  font-weight: bold;
+}
+.red {
+  color: #f5222d;
+}
+.orange {
+  color: #fa8c16;
+}
+.yellow {
+  color: #faad14;
+}
+.blue {
+  color: #1890ff;
+}
+
+.van-cell {
+  font-size: 26px !important;
+  padding: 20px !important;
+}
+.van-cell.active {
+  background-color: #e8f3ff !important;
+  color: #1677ff !important;
+}
+</style>

+ 27 - 1
src/views/weibao/router/router.js

@@ -55,6 +55,9 @@ const routes = [{
     {
         path: '/followUp/record',
         name: 'record',
+        meta: {
+            keepAlive: true
+        },
         component: () => import('../views/followUp/record.vue')
     },
     {
@@ -69,9 +72,32 @@ const routes = [{
     },
     {
         path: '/followUp/addfollow',
-        name: 'historySupervisor',
+        name: 'addfollow',
         component: () => import('../views/followUp/addfollow.vue')
     },
+    {
+        path: '/followUp/addfollowwarn',
+        name: 'addfollowwarn',
+        meta: {
+            keepAlive: true
+        },
+        component: () => import('../views/followUp/addfollowwarn.vue')
+    },
+    {
+        path: '/followUp/addNote',
+        name: 'addNote',
+        component: () => import('../views/followUp/addNote.vue')
+    },
+    {
+        path: '/followUp/checkNote',
+        name: 'checkNote',
+        component: () => import('../views/followUp/checkNote.vue')
+    },
+    {
+        path: '/followUp/warningDetail',
+        name: 'warningDetail',
+        component: () => import('../views/followUp/warningDetail.vue')
+    },
     {
         path: '/specialServices/index',
         name: 'specialServices',

+ 341 - 0
src/views/weibao/views/followUp/addNote.vue

@@ -0,0 +1,341 @@
+<template>
+  <div>
+    <van-nav-bar
+      title="新增处置记录"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
+
+    <van-form @submit="warningRecordSave">
+      <div class="main">
+        <!-- 处置情况 -->
+        <van-row class="cell">
+          <van-col span="6" class="left">处置情况:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.disposalStatus"
+              placeholder="请输入处置情况"
+              :rules="[{ required: true }]"
+              type="textarea"
+              rows="2"
+            />
+          </van-col>
+        </van-row>
+
+        <!-- 处置措施 -->
+        <van-row class="cell">
+          <van-col span="6" class="left">处置措施:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.disposalMeasures"
+              placeholder="请输入处置措施"
+              :rules="[{ required: true }]"
+              type="textarea"
+              rows="2"
+            />
+          </van-col>
+        </van-row>
+
+        <!-- 处置时间 -->
+        <van-row class="cell">
+          <van-col span="6" class="left">处置时间:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.disposalDate"
+              @click="showDatePopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+              is-link
+            />
+          </van-col>
+        </van-row>
+        <van-popup v-model="showDate" position="bottom" :style="{ height: '50%' }">
+          <van-datetime-picker
+            @confirm="dateConfirm"
+            @cancel="showDatePopup"
+            v-model="selectDate"
+            type="date"
+            :min-date="minDate"
+            :max-date="maxDate"
+            :columns-order="['year', 'month', 'day']"
+            :formatter="formatter"
+          />
+        </van-popup>
+
+        <!-- 处置结果 -->
+        <van-row class="cell">
+          <van-col span="6" class="left">处置结果:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.disposalResult"
+              @click="showResultPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+              is-link
+            />
+          </van-col>
+        </van-row>
+        <van-popup v-model="showResult" position="bottom" :style="{ height: '50%' }">
+          <van-picker
+            show-toolbar
+            :columns="resultList"
+            @confirm="resultConfirm"
+            @cancel="showResultPopup"
+          />
+        </van-popup>
+
+        <!-- 原预警级别 -->
+        <van-row v-if="form.disposalResult === '预警升降级'" class="cell">
+          <van-col span="6" class="left">原预警级别:</van-col>
+          <van-col span="18">
+            <van-field v-model="oldWarningText" readonly />
+          </van-col>
+        </van-row>
+
+        <!-- 新预警级别 -->
+        <van-row v-if="form.disposalResult === '预警升降级'" class="cell">
+          <van-col span="6" class="left">新预警级别:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="newWarningText"
+              @click="showLevelPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+              is-link
+            />
+          </van-col>
+        </van-row>
+        <van-popup v-model="showLevel" position="bottom" :style="{ height: '50%' }">
+          <van-picker
+            show-toolbar
+            :columns="levelList"
+            @confirm="levelConfirm"
+            @cancel="showLevelPopup"
+          />
+        </van-popup>
+
+        <!-- 上报情况 -->
+        <van-row v-if="form.disposalResult !== '持续跟踪'" class="cell">
+          <van-col span="6" class="left">上报情况:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.reportDetail"
+              placeholder="请输入上报情况"
+              :rules="[{ required: true }]"
+              type="textarea"
+              rows="2"
+            />
+          </van-col>
+        </van-row>
+
+        <!-- 上报平台 -->
+        <van-row v-if="form.disposalResult !== '持续跟踪'" class="cell">
+          <van-col span="6" class="left">上报平台:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.reportPlatform"
+              placeholder="请输入上报平台"
+              :rules="[{ required: true }]"
+            />
+          </van-col>
+        </van-row>
+
+        <!-- 备注 -->
+        <van-row v-if="form.disposalResult !== '持续跟踪'" class="cell">
+          <van-col span="6" class="left">备注:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.remark"
+              placeholder="请输入备注"
+              type="textarea"
+              rows="2"
+            />
+          </van-col>
+        </van-row>
+      </div>
+
+      <div class="but">
+        <van-button native-type="submit" :disabled="disabled" class="submit-btn">
+          提 交
+        </van-button>
+      </div>
+    </van-form>
+  </div>
+</template>
+
+<script>
+import { warningRecordSave } from "../../api/index.js";
+
+export default {
+  data() {
+    return {
+      disabled: false,
+      showDate: false,
+      showResult: false,
+      showLevel: false,
+
+      selectDate: new Date(),
+      minDate: new Date(2020, 0, 1),
+      maxDate: new Date(),
+
+      resultList: [
+        { text: "持续跟踪", value: "持续跟踪" },
+        { text: "预警升降级", value: "预警升降级" },
+        { text: "风险解除", value: "风险解除" },
+      ],
+      levelList: [
+        { text: "蓝色预警", value: 1 },
+        { text: "黄色预警", value: 2 },
+        { text: "橙色预警", value: 3 },
+        { text: "红色预警", value: 4 },
+      ],
+
+      form: {
+        disposalStatus: "",
+        disposalMeasures: "",
+        disposalDate: this.timeFormat(new Date()),
+        disposalResult: "",
+        warningLevel: "",
+        reportDetail: "",
+        reportPlatform: "",
+        remark: "",
+        warningId: "",
+        reportReceiver: "",
+        employeeName: "",
+      },
+    };
+  },
+
+  computed: {
+    oldWarningText() {
+      const level = this.$route.query.warningLevel;
+      const map = { 1: "蓝色预警", 2: "黄色预警", 3: "橙色预警", 4: "红色预警" };
+      return map[level] || "";
+    },
+    newWarningText() {
+      const map = { 1: "蓝色预警", 2: "黄色预警", 3: "橙色预警", 4: "红色预警" };
+      return map[this.form.warningLevel] || "请选择";
+    },
+  },
+
+  created() {
+    this.form.warningId = this.$route.query.warningId || "";
+    this.form.employeeName = this.$route.query.employeeName || "";
+  },
+
+  methods: {
+    timeFormat(time) {
+      let year = time.getFullYear();
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      if (month < 10) month = "0" + month;
+      if (day < 10) day = "0" + day;
+      return `${year}-${month}-${day}`;
+    },
+
+    formatter(type, val) {
+      if (type === "year") return val + "年";
+      if (type === "month") return val + "月";
+      if (type === "day") return val + "日";
+      return val;
+    },
+
+    showDatePopup() {
+      this.showDate = !this.showDate;
+    },
+    dateConfirm(v) {
+      this.form.disposalDate = this.timeFormat(v);
+      this.showDate = false;
+    },
+
+    showResultPopup() {
+      this.showResult = !this.showResult;
+    },
+    resultConfirm(value) {
+      this.form.disposalResult = value.value;
+      this.showResult = false;
+    },
+
+    showLevelPopup() {
+      this.showLevel = !this.showLevel;
+    },
+    levelConfirm(value) {
+      this.form.warningLevel = value.value;
+      this.showLevel = false;
+    },
+
+    // 提交接口
+    warningRecordSave() {
+      this.$dialog
+        .confirm({
+          title: "是否确认提交?",
+          message: `提交后不可修改,请确认!`,
+        })
+        .then(async () => {
+          this.disabled = true;
+          try {
+            const params = {
+              ...this.form,
+              disposalDate: this.form.disposalDate + " 00:00:00",
+            };
+            const res = await warningRecordSave(params);
+            if (res.code === "0000") {
+              this.$toast.success("保存成功");
+              this.$router.back();
+            }
+          } catch (e) {
+            this.$toast.fail("保存失败");
+          } finally {
+            this.disabled = false;
+          }
+        })
+        .catch(() => {});
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.main {
+  padding: 0 30px;
+}
+.cell {
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid #eee;
+  padding: 12px 0;
+  font-size: 30px;
+
+  .left {
+    display: flex;
+    align-items: center;
+    height: 100%;
+    font-size: 30px;
+  }
+}
+
+.but {
+  text-align: center;
+  padding: 30px 0;
+}
+
+.submit-btn {
+  width: 90%;
+  height: 88px;
+  font-size: 32px;
+  border-radius: 12px;
+  background: #1677ff;
+  border: none;
+  color: #ffffff;
+}
+
+::v-deep .van-field__control {
+  font-size: 30px;
+}
+</style>

+ 165 - 90
src/views/weibao/views/followUp/addfollow.vue

@@ -1,19 +1,43 @@
 <template>
   <div>
-    <van-nav-bar title="新建随访" @click-left="$router.back()" left-arrow fixed placeholder safe-area-inset-top />
+    <van-nav-bar
+      title="新建随访"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
     <div class="main">
       <van-form @submit="saveFollowUp">
         <van-row class="cell">
           <van-col span="4">姓名:</van-col>
-          <van-col span="8" @click="searchshow=true">
-            <van-field v-model="form.name" placeholder="请选择" :rules="[{ required: true}]" readonly />
+          <van-col span="8" @click="searchshow = true">
+            <van-field
+              v-model="form.name"
+              placeholder="请选择"
+              :rules="[{ required: true }]"
+              readonly
+            />
           </van-col>
           <van-popup position="bottom" v-model="searchshow" :style="{ height: '50%' }">
-            <van-search placeholder="请输入姓名查询" @input="listChildInfoByName" v-model="positionSearch" />
-            <van-picker value-key="name" item-height="48" visible-item-count="5" show-toolbar :columns="childList" @confirm="onConfirmPicker" @cancel="showPicker = false">
+            <van-search
+              placeholder="请输入姓名查询"
+              @input="listChildInfoByName"
+              v-model="positionSearch"
+            />
+            <van-picker
+              value-key="name"
+              item-height="48"
+              visible-item-count="5"
+              show-toolbar
+              :columns="childList"
+              @confirm="onConfirmPicker"
+              @cancel="showPicker = false"
+            >
               <template #option="item">
-                <div>{{item.name}}</div>
-                <div>({{item.idCard}})</div>
+                <div>{{ item.name }}</div>
+                <div>({{ item.idCard }})</div>
               </template>
             </van-picker>
             <!-- <van-picker v-show="streetList.length>0" show-toolbar :columns="childList" @confirm="onConfirm" @cancel="streetshowPopup" @change="onChange" value-key="name" /> -->
@@ -25,7 +49,7 @@
         </van-row>
         <van-row class="cell">
           <van-col span="4">年龄:</van-col>
-          <van-col span="8" @click="searchshow=true">
+          <van-col span="8" @click="searchshow = true">
             <van-field v-model="form.age" placeholder="请选择" disabled />
           </van-col>
           <van-col span="6">性别:</van-col>
@@ -42,7 +66,11 @@
         <van-row class="cell">
           <van-col span="6">健康情况:</van-col>
           <van-col span="18">
-            <van-field v-model="['','健康','患病','残疾'][form.healthStatus]" placeholder="健康情况" disabled />
+            <van-field
+              v-model="['', '健康', '患病', '残疾'][form.healthStatus]"
+              placeholder="健康情况"
+              disabled
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
@@ -54,7 +82,11 @@
         <van-row class="cell">
           <van-col span="9">监护人联系方式:</van-col>
           <van-col span="15">
-            <van-field v-model="form.guardianPhone" placeholder="监护人联系方式" disabled />
+            <van-field
+              v-model="form.guardianPhone"
+              placeholder="监护人联系方式"
+              disabled
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
@@ -72,65 +104,108 @@
         <van-row class="cell">
           <van-col span="6">随访时间:</van-col>
           <van-col span="18">
-            <van-field v-model="form.followDate" @click="showPopup" placeholder="请选择" :rules="[{ required: true}]" readonly />
+            <van-field
+              v-model="form.followDate"
+              @click="showPopup"
+              placeholder="请选择"
+              :rules="[{ required: true }]"
+              readonly
+            />
           </van-col>
           <!-- <van-col span="18" @click="showPopup">{{form.followDate?form.followDate:'请选择'}}</van-col> -->
           <van-popup v-model="show" position="bottom" :style="{ height: '50%' }">
-            <van-datetime-picker @confirm="dateConfirm" @cancel="showPopup" v-model="followDate" type="date" :min-date="minDate" :max-date="maxDate" :columns-order="[ 'year','month', 'day']"
-              :formatter="formatter" />
+            <van-datetime-picker
+              @confirm="dateConfirm"
+              @cancel="showPopup"
+              v-model="followDate"
+              type="date"
+              :min-date="minDate"
+              :max-date="maxDate"
+              :columns-order="['year', 'month', 'day']"
+              :formatter="formatter"
+            />
           </van-popup>
         </van-row>
         <van-row class="cell">
           <van-col span="6">选择街道:</van-col>
           <van-col span="18">
-            <van-field v-model="form.streetName" @click="streetshowPopup" placeholder="请选择" :rules="[{ required: true}]" readonly />
+            <van-field
+              v-model="form.streetName"
+              @click="streetshowPopup"
+              placeholder="请选择"
+              :rules="[{ required: true }]"
+              readonly
+            />
           </van-col>
           <!-- <van-col span="18" @click="streetshowPopup">{{form.streetName?form.streetName:'请选择'}}</van-col> -->
           <van-popup v-model="streetShow" position="bottom" :style="{ height: '50%' }">
-            <van-picker default-index="2" show-toolbar :columns="streetList" @confirm="onConfirm" @cancel="streetshowPopup" @change="onChange" value-key="name" />
+            <van-picker
+              default-index="2"
+              show-toolbar
+              :columns="streetList"
+              @confirm="onConfirm"
+              @cancel="streetshowPopup"
+              @change="onChange"
+              value-key="name"
+            />
           </van-popup>
         </van-row>
         <van-row class="cell">
           <van-col span="6">随访地点:</van-col>
           <van-col span="18">
-            <van-field v-model="form.place" placeholder="请输入随访地点" :rules="[{ required: true}]" />
+            <van-field
+              v-model="form.place"
+              placeholder="请输入随访地点"
+              :rules="[{ required: true }]"
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
           <van-col span="6">随访人员:</van-col>
           <van-col span="18">
-            <van-field v-model="form.personnel" placeholder="请输入" :rules="[{ required: true}]" />
+            <van-field
+              v-model="form.personnel"
+              placeholder="请输入"
+              :rules="[{ required: true }]"
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
           <van-col span="6">联系方式:</van-col>
           <van-col span="18">
-            <van-field v-model="form.phone" type="tel" placeholder="请输入手机号" :rules="[{validator,required: true, message:'手机号格式不正确'}]" />
+            <van-field
+              v-model="form.phone"
+              type="tel"
+              placeholder="请输入手机号"
+              :rules="[{ validator, required: true, message: '手机号格式不正确' }]"
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
           <van-col span="6">随访记录:</van-col>
           <van-col span="18">
-            <van-field class="textarea" :rules="[{ required: true}]" v-model="form.remark" rows="1" type="textarea" placeholder="请输入随访记录" />
+            <van-field
+              class="textarea"
+              :rules="[{ required: true }]"
+              v-model="form.remark"
+              rows="1"
+              type="textarea"
+              placeholder="请输入随访记录"
+            />
           </van-col>
         </van-row>
         <vanUploader :text="'图片'" @getfileList="getfileList"></vanUploader>
         <vanUploader :text="'视频'" @getfileList="getfileList"></vanUploader>
-        <div style="text-align:center;">
+        <div style="text-align: center">
           <van-button native-type="submit" :disabled="disabled">确 定</van-button>
         </div>
       </van-form>
     </div>
-
   </div>
 </template>
 <script>
-import {
-  saveFollowUp,
-  getStreetList,
-  listChildInfoByName,
-} from '../../api/index.js'
-import vanUploader from '../../components/vanUploader.vue'
+import { saveFollowUp, getStreetList, listChildInfoByName } from "../../api/index.js";
+import vanUploader from "../../components/vanUploader.vue";
 export default {
   components: { vanUploader },
   data() {
@@ -143,127 +218,127 @@ export default {
       form: {
         fileList: [],
       },
-      streetList: '',
+      streetList: "",
       followDate: new Date(),
       minDate: new Date(2020, 0, 1),
       maxDate: new Date(),
-      positionSearch: '',
+      positionSearch: "",
       childList: [],
       pics: [],
       videos: [],
-    }
+    };
   },
   created() {
-    this.getStreetList()
+    this.getStreetList();
   },
   methods: {
     async listChildInfoByName() {
-      if (!this.positionSearch) return
-      const res = await listChildInfoByName({ name: this.positionSearch })
-      this.childList = res.data
+      if (!this.positionSearch) return;
+      const res = await listChildInfoByName({ name: this.positionSearch });
+      this.childList = res.data;
     },
 
     validator(val) {
-      return /^1(3|4|5|6|7|8|9)\d{9}$/.test(val)
+      return /^1(3|4|5|6|7|8|9)\d{9}$/.test(val);
     },
     formatter(type, val) {
-      if (type === 'year') {
-        return val + '年'
+      if (type === "year") {
+        return val + "年";
       }
-      if (type === 'month') {
-        return val + '月'
+      if (type === "month") {
+        return val + "月";
       }
-      if (type === 'day') {
-        return val + '日'
+      if (type === "day") {
+        return val + "日";
       }
-      return val
+      return val;
     },
     timeFormat(time) {
       // 时间格式化 2019-09-08
-      let year = time.getFullYear()
-      let month = time.getMonth() + 1
+      let year = time.getFullYear();
+      let month = time.getMonth() + 1;
       if (month < 10) {
-        month = '0' + month
+        month = "0" + month;
       }
 
-      let day = time.getDate()
+      let day = time.getDate();
       if (day < 10) {
-        day = '0' + day
+        day = "0" + day;
       }
-      return year + '-' + month + '-' + day
+      return year + "-" + month + "-" + day;
     },
     showPopup() {
-      this.show = !this.show
+      this.show = !this.show;
     },
     streetshowPopup() {
-      this.streetShow = !this.streetShow
+      this.streetShow = !this.streetShow;
     },
     onChange() {},
     onConfirm(value) {
-      this.form.streetName = value.name
-      this.form.areaId = value.id
-      this.streetShow = false
+      this.form.streetName = value.name;
+      this.form.areaId = value.id;
+      this.streetShow = false;
     },
     onConfirmPicker(value) {
-      this.form.childId = value.id
-      this.form.name = value.name
-      this.form.typeDesc = value.typeDesc
-      this.form.age = value.age + '岁'
-      this.form.genderDesc = value.genderDesc
-      this.form.school = value.educationLevel
-      this.form.healthStatus = value.healthStatus
-      this.form.address = value.address
-      this.form.tutelageName = value.tutelageName
-      this.form.guardianName = value.guardianName
-      this.form.guardianPhone = value.guardianPhone
-      this.searchshow = false
+      this.form.childId = value.id;
+      this.form.name = value.name;
+      this.form.typeDesc = value.typeDesc;
+      this.form.age = value.age + "岁";
+      this.form.genderDesc = value.genderDesc;
+      this.form.school = value.educationLevel;
+      this.form.healthStatus = value.healthStatus;
+      this.form.address = value.address;
+      this.form.tutelageName = value.tutelageName;
+      this.form.guardianName = value.guardianName;
+      this.form.guardianPhone = value.guardianPhone;
+      this.searchshow = false;
     },
     dateConfirm(v) {
-      console.log(v)
-      this.form.followDate = this.timeFormat(v)
-      this.show = false
+      console.log(v);
+      this.form.followDate = this.timeFormat(v);
+      this.show = false;
     },
     async saveFollowUp() {
       if (this.pics.length == 0) {
-        this.$toast.fail('请选择至少一张图片')
-        return
+        this.$toast.fail("请选择至少一张图片");
+        return;
       }
-      this.form.pics = this.pics.join(',')
-      this.form.videos = this.videos.join(',')
-      var data = JSON.parse(JSON.stringify(this.form))
-      data.followDate = data.followDate + ' 00:00:00'
-      const res = await saveFollowUp(data)
-      if (res.code == '0000') {
-        this.$toast.success('成功')
-        this.$router.go(-1)
+      this.form.pics = this.pics.join(",");
+      this.form.videos = this.videos.join(",");
+      var data = JSON.parse(JSON.stringify(this.form));
+      data.followDate = data.followDate + " 00:00:00";
+      const res = await saveFollowUp(data);
+      if (res.code == "0000") {
+        this.$toast.success("成功");
+        this.$router.go(-1);
       }
     },
     async getStreetList() {
-      const res = await getStreetList()
-      this.streetList = res.data
+      const res = await getStreetList();
+      this.streetList = res.data;
     },
     getPositionList() {},
     getfileList(v, index) {
       if (index == undefined) {
         if (
-          v.indexOf('.png') != -1 ||
-          v.indexOf('.jpg') != -1 ||
-          v.indexOf('.jpeg') != -1
+          v.indexOf(".png") != -1 ||
+          v.indexOf(".jpg") != -1 ||
+          v.indexOf(".jpeg") != -1
         ) {
-          this.pics.push(v)
+          this.pics.push(v);
         } else {
-          this.videos.push(v)
+          this.videos.push(v);
         }
       } else {
-        if (v == '图片') {
-          this.pics.splice(index, 1)
+        if (v == "图片") {
+          this.pics.splice(index, 1);
         } else {
-          this.videos.splice(index, 1)
+          this.videos.splice(index, 1);
         }
       }
     },
   },
-}
+};
 </script>
 <style lang="scss" scoped>
 .tx {
@@ -372,4 +447,4 @@ export default {
   color: #333;
   -webkit-text-fill-color: unset;
 }
-</style>
+</style>

+ 668 - 0
src/views/weibao/views/followUp/addfollowwarn.vue

@@ -0,0 +1,668 @@
+<template>
+  <div>
+    <van-nav-bar
+      title="新建随访"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
+
+    <!-- 姓名搜索框 -->
+    <div class="search-name-wrapper" @click="searchshow = true">
+      <van-field
+        v-model="form.name"
+        placeholder="请点击选择儿童"
+        readonly
+        is-link
+        label="姓名"
+        label-width="120px"
+      />
+    </div>
+
+    <!-- 儿童选择弹窗 -->
+    <van-popup position="bottom" v-model="searchshow" :style="{ height: '50%' }">
+      <van-search
+        placeholder="请输入姓名查询"
+        @input="listChildInfoByName"
+        v-model="positionSearch"
+        style="padding: 10px 15px"
+      />
+      <van-picker
+        value-key="name"
+        item-height="48"
+        visible-item-count="5"
+        show-toolbar
+        :columns="childList"
+        @confirm="onConfirmPicker"
+        @cancel="searchshow = false"
+      >
+        <template #option="item">
+          <div style="padding: 0 20px; font-size: 17px">
+            {{ item.name }} ({{ item.idCard }})
+          </div>
+        </template>
+      </van-picker>
+    </van-popup>
+
+    <!-- 优化顶部卡片 + 保留头像 + 默认占位 -->
+    <div class="info-card">
+      <div class="card-top">
+        <div class="avatar-wrap">
+          <img
+            class="avatar"
+            :src="childInfo?.url ? childInfo.url : require('../../assets/tx.png')"
+            alt=""
+          />
+        </div>
+        <div class="base-info">
+          <div class="info-row">
+            <span class="label1">姓名:</span>
+            <span class="val">{{ childInfo?.name || "未选择" }}</span>
+          </div>
+          <div class="info-row">
+            <span class="label1">性别:</span>
+            <span class="val">{{ childInfo?.sex || "-" }}</span>
+            <span class="label1" style="margin-left: 20px">年龄:</span>
+            <span class="val">{{ childInfo?.age || "-" }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="card-bottom">
+        <div class="info-row">
+          <span class="label">儿童类型:</span>
+          <span class="val">{{ childInfo?.childTypeName || "-" }}</span>
+        </div>
+        <div class="info-row">
+          <span class="label">教育情况:</span>
+          <span class="val">{{ childInfo?.school || "-" }}</span>
+        </div>
+        <div class="info-row">
+          <span class="label">健康情况:</span>
+          <span class="val">{{ childInfo?.healthStatus || "-" }}</span>
+        </div>
+        <div class="info-row">
+          <span class="label">监护人:</span>
+          <span class="val">{{ childInfo?.guardianName || "-" }}</span>
+        </div>
+        <div class="info-row">
+          <span class="label">联系方式:</span>
+          <span class="val">{{ childInfo?.guardianPhone || "-" }}</span>
+        </div>
+        <div class="info-row">
+          <span class="label">居住地址:</span>
+          <span class="val">{{ childInfo?.address || "-" }}</span>
+        </div>
+      </div>
+    </div>
+
+    <van-form @submit="saveFollowUp">
+      <div class="main">
+        <van-row class="cell">
+          <van-col span="6">随访时间:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.followDate"
+              @click="showPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+            />
+          </van-col>
+          <van-popup v-model="show" position="bottom" :style="{ height: '50%' }">
+            <van-datetime-picker
+              @confirm="dateConfirm"
+              @cancel="showPopup"
+              v-model="followDate"
+              type="date"
+              :min-date="minDate"
+              :max-date="maxDate"
+              :columns-order="['year', 'month', 'day']"
+              :formatter="formatter"
+            />
+          </van-popup>
+        </van-row>
+        <van-row class="cell">
+          <van-col span="6">选择街道:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.streetName"
+              @click="streetshowPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+            />
+          </van-col>
+          <van-popup v-model="streetShow" position="bottom" :style="{ height: '50%' }">
+            <van-picker
+              default-index="2"
+              :visible-item-count="6"
+              show-toolbar
+              :columns="streetList"
+              @confirm="onConfirm"
+              @cancel="streetshowPopup"
+              @change="onChange"
+              value-key="name"
+            />
+          </van-popup>
+        </van-row>
+        <van-row class="cell">
+          <van-col span="6">随访地点:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.place"
+              :rules="[{ required: true }]"
+              placeholder="请输入随访地点"
+            />
+          </van-col>
+        </van-row>
+        <van-row class="cell">
+          <van-col span="6">随访人员:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.personnel"
+              :rules="[{ required: true }]"
+              placeholder="请输入随访人员"
+            />
+          </van-col>
+        </van-row>
+        <van-row class="cell">
+          <van-col span="6">联系方式:</van-col>
+          <van-col span="18">
+            <van-field
+              v-model="form.phone"
+              type="tel"
+              :rules="[{ validator, required: true, message: '手机号格式不正确' }]"
+              placeholder="请输入"
+            />
+          </van-col>
+        </van-row>
+        <van-row class="cell">
+          <van-col span="6">随访记录:</van-col>
+          <van-col span="18">
+            <van-field
+              class="textarea"
+              :rules="[{ required: true }]"
+              v-model="form.remark"
+              rows="1"
+              type="textarea"
+              placeholder="请输入随访记录"
+            />
+          </van-col>
+        </van-row>
+
+        <van-row v-if="childInfo && !childInfo.warningId" class="cell">
+          <van-col span="6">预警风险:</van-col>
+          <van-col span="18">
+            <van-field
+              placeholder="请选择预警风险"
+              :value="riskText"
+              readonly
+              is-link
+              @click="openWarningSheet"
+            />
+          </van-col>
+        </van-row>
+        <vanUploader
+          ref="imgUploader"
+          :text="'图片'"
+          @getfileList="getfileList"
+        ></vanUploader>
+        <vanUploader
+          ref="videoUploader"
+          :text="'视频'"
+          @getfileList="getfileList"
+        ></vanUploader>
+
+        <van-row v-if="childInfo && childInfo.warningLevel" class="cell">
+          <van-col span="6">预警等级:</van-col>
+          <van-col span="18">
+            <van-field
+              :value="warningText"
+              readonly
+              is-link
+              @click="checkWarningDetail()"
+            />
+          </van-col>
+        </van-row>
+
+        <van-row v-for="(item, index) in warningRecordList" :key="item.id" class="cell">
+          <van-col span="6">查看处置{{ index + 1 }}:</van-col>
+          <van-col span="18">
+            <van-field
+              :value="item.disposalResult"
+              readonly
+              is-link
+              @click="checkNoteHandle(item)"
+            />
+          </van-col>
+        </van-row>
+
+        <van-row v-if="childInfo && childInfo.warningId && isAddRecord" class="cell">
+          <van-col span="6">添加处置:</van-col>
+          <van-col span="18">
+            <van-field
+              placeholder="请添加处置记录"
+              readonly
+              is-link
+              @click="addNoteHandle"
+            />
+          </van-col>
+        </van-row>
+      </div>
+
+      <div class="but">
+        <van-button native-type="submit" :disabled="disabled || !childInfo">
+          确 定
+        </van-button>
+      </div>
+    </van-form>
+
+    <WarningSheet
+      v-model="showWarningSheet"
+      :list="warningList"
+      :selected-value="riskValue"
+      @confirm="handleWarningConfirm"
+    ></WarningSheet>
+  </div>
+</template>
+
+<script>
+import {
+  saveFollowUp,
+  getStreetList,
+  getChildInfo,
+  warningReasons,
+  warningSave,
+  warningRecordList,
+  listChildInfoByName,
+} from "../../api/index.js";
+import WarningSheet from "../../components/warningSheet.vue";
+import vanUploader from "../../components/vanUploader.vue";
+
+export default {
+  name: "AddFollowWarn",
+  components: { vanUploader, WarningSheet },
+  data() {
+    return {
+      show: false,
+      streetShow: false,
+      disabled: false,
+      showvideoplay: false,
+      videourl: "",
+      fileList: [],
+      form: { fileList: [] },
+      streetList: "",
+      followDate: new Date(),
+      minDate: new Date(2020, 0, 1),
+      maxDate: new Date(),
+      customFieldName: { text: "name", id: "id" },
+      childInfo: "",
+      pics: [],
+      videos: [],
+
+      showWarningSheet: false,
+      riskText: "",
+      warningList: [],
+      riskValue: "",
+      riskLabel: "",
+
+      isAddRecord: false,
+      warningRecordList: [],
+
+      searchshow: false,
+      positionSearch: "",
+      childList: [],
+    };
+  },
+
+  activated() {
+    this.loadInitData();
+    if (this.childInfo?.warningId) {
+      this.getWarningRecordList();
+    }
+  },
+
+  beforeRouteEnter(to, from, next) {
+    next((vm) => {
+      if (from.path === "/followUp/director" || from.path === "/") {
+        vm.resetPage();
+      }
+    });
+  },
+
+  beforeRouteLeave(to, from, next) {
+    if (to.path === "/followUp/director") {
+      this.childInfo = null;
+    }
+    next();
+  },
+
+  computed: {
+    warningText() {
+      const level = this.childInfo?.warningLevel;
+      const map = { 1: "蓝色预警", 2: "黄色预警", 3: "橙色预警", 4: "红色预警" };
+      return map[level] || "";
+    },
+  },
+
+  methods: {
+    loadInitData() {
+      if (this.streetList.length === 0) {
+        this.getStreetList();
+      }
+      if (this.warningList.length === 0) {
+        this.getWarningReasons();
+      }
+    },
+
+    resetPage() {
+      this.childInfo = "";
+      this.pics = [];
+      this.videos = [];
+      this.warningRecordList = [];
+      this.form = { fileList: [] };
+      this.riskText = "";
+      this.isAddRecord = false;
+      this.disabled = false;
+      this.positionSearch = "";
+      this.childList = [];
+      this.followDate = new Date();
+    },
+
+    // 选择儿童时 → 清空之前所有数据
+    async onConfirmPicker(value) {
+      // 清空表单、图片、视频、预警
+      this.form = { fileList: [] };
+      this.pics = [];
+      this.videos = [];
+      this.riskText = "";
+      this.warningRecordList = [];
+      this.followDate = new Date();
+      this.disabled = false;
+
+      // 清空图片/视频上传组件的预览
+      if (this.$refs.imgUploader) this.$refs.imgUploader.childrenClear();
+      if (this.$refs.videoUploader) this.$refs.videoUploader.childrenClear();
+
+      this.form.name = value.name;
+      this.searchshow = false;
+      await this.getChildInfoById(value.id);
+    },
+
+    async getChildInfoById(id) {
+      const res = await getChildInfo({ id });
+      this.childInfo = res.data;
+      this.form.childId = res.data.childId;
+      this.getWarningRecordList();
+    },
+
+    async listChildInfoByName() {
+      if (!this.positionSearch) return;
+      const res = await listChildInfoByName({ name: this.positionSearch });
+      this.childList = res.data;
+    },
+
+    openWarningSheet() {
+      this.showWarningSheet = true;
+    },
+    async handleWarningConfirm({ item, levelText }) {
+      try {
+        const res = await warningSave({
+          childId: this.form.childId,
+          warningReason: item.label,
+        });
+        if (res.code === "0000") {
+          this.$toast.success("保存成功");
+          this.showWarningSheet = false;
+          this.childInfo.warningId = res.data.id;
+          this.childInfo.warningLevel = res.data.warningLevel;
+          this.riskText = levelText;
+          this.getWarningRecordList();
+        }
+      } catch (err) {
+        this.$toast.fail("保存失败");
+      }
+    },
+    checkWarningDetail() {
+      this.$router.push({
+        path: "/followUp/warningDetail",
+        query: { id: this.childInfo.warningId },
+      });
+    },
+    addNoteHandle() {
+      this.$router.push({
+        path: "/followUp/addNote",
+        query: {
+          warningId: this.childInfo.warningId,
+          warningLevel: this.childInfo.warningLevel,
+        },
+      });
+    },
+    checkNoteHandle(e) {
+      this.$router.push({
+        path: "/followUp/checkNote",
+        query: { id: e.id, warningLevel: this.childInfo.warningLevel },
+      });
+    },
+    validator(val) {
+      return /^1(3|4|5|6|7|8|9)\d{9}$/.test(val);
+    },
+    async getChildInfo() {},
+    formatter(type, val) {
+      if (type === "year") return val + "年";
+      if (type === "month") return val + "月";
+      if (type === "day") return val + "日";
+      return val;
+    },
+    timeFormat(time) {
+      let year = time.getFullYear();
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      if (month < 10) month = "0" + month;
+      if (day < 10) day = "0" + day;
+      return year + "-" + month + "-" + day;
+    },
+    showPopup() {
+      this.show = !this.show;
+    },
+    streetshowPopup() {
+      this.streetShow = !this.streetShow;
+    },
+    onChange() {},
+    onConfirm(value) {
+      this.form.streetName = value.name;
+      this.form.areaId = value.id;
+      this.streetShow = false;
+    },
+    dateConfirm(v) {
+      this.form.followDate = this.timeFormat(v);
+      this.show = false;
+    },
+    async saveFollowUp() {
+      if (!this.childInfo) {
+        this.$toast.fail("请先选择儿童");
+        return;
+      }
+      if (this.pics.length == 0) {
+        this.$toast.fail("请选择至少一张图片");
+        return;
+      }
+      this.form.pics = this.pics.join(",");
+      this.form.videos = this.videos.join(",");
+      this.form.warningId = this.childInfo.warningId;
+      this.form.warningLevel = this.childInfo.warningLevel;
+      var data = JSON.parse(JSON.stringify(this.form));
+      data.followDate = data.followDate + " 00:00:00";
+      const res = await saveFollowUp(data);
+      if (res.code == "0000") {
+        this.disabled = true;
+        this.$toast.success("成功");
+        this.$router.go(-1);
+      }
+    },
+    async getStreetList() {
+      const res = await getStreetList();
+      this.streetList = res.data;
+    },
+    getfileList(v, index) {
+      if (index == undefined) {
+        if (
+          v.indexOf(".png") != -1 ||
+          v.indexOf(".jpg") != -1 ||
+          v.indexOf(".jpeg") != -1
+        )
+          this.pics.push(v);
+        else this.videos.push(v);
+      } else {
+        v == "图片" ? this.pics.splice(index, 1) : this.videos.splice(index, 1);
+      }
+    },
+    async getWarningRecordList() {
+      if (!this.childInfo?.warningId) return;
+      const res = await warningRecordList({
+        id: this.childInfo.warningId,
+        pageNum: 1,
+        pageSize: 30,
+      });
+      if (res.code == "0000") {
+        this.warningRecordList = res.data.records;
+        this.checkIsAddRecord();
+      }
+    },
+    async getWarningReasons() {
+      const res = await warningReasons();
+      let arr = [];
+      for (let key in res.data) arr.push({ value: key, label: res.data[key] });
+      this.warningList = arr;
+    },
+    checkIsAddRecord() {
+      if (!this.warningRecordList || this.warningRecordList.length === 0) {
+        this.isAddRecord = true;
+        return;
+      }
+      const temp = this.warningRecordList.some(
+        (item) => item.disposalResult === "风险解除"
+      );
+      this.isAddRecord = !temp;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+/* 搜索框 */
+.search-name-wrapper {
+  padding: 20px 30px;
+  background: #fff;
+  border-bottom: 1px solid #eee;
+  ::v-deep .van-field {
+    font-size: 30px;
+  }
+  ::v-deep .van-field__label {
+    font-size: 30px;
+    width: 120px !important;
+  }
+  ::v-deep .van-field__control {
+    font-size: 30px;
+  }
+}
+
+/* 顶部信息卡片 */
+.info-card {
+  margin: 15px 25px;
+  padding: 15px;
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.06);
+  font-size: 30px;
+}
+.card-top {
+  display: flex;
+  align-items: center;
+  margin-bottom: 15px;
+}
+.avatar-wrap {
+  margin-right: 20px;
+}
+.avatar {
+  width: 125px;
+  height: 125px;
+  border-radius: 8px;
+  object-fit: cover;
+}
+.base-info {
+  flex: 1;
+}
+.info-row {
+  display: flex;
+  align-items: flex-start;
+  line-height: 1.9;
+
+  .label1 {
+    width: 100px;
+    white-space: nowrap;
+    flex-shrink: 0;
+  }
+  .label {
+    white-space: nowrap;
+    width: 160px;
+    color: #464646;
+    margin-right: 8px;
+    flex-shrink: 0;
+  }
+  .val {
+    flex: 1;
+    color: #2e2e2e;
+    word-break: break-all;
+    text-align: left;
+  }
+}
+.card-bottom {
+  border-top: 1px solid #f0f0f0;
+  padding-top: 15px;
+}
+
+/* 表单样式 */
+.main {
+  padding: 0 30px;
+}
+.cell {
+  border-bottom: 1px solid #eee;
+  margin-top: 16px;
+  .van-col {
+    line-height: 80px;
+    font-size: 30px;
+  }
+  .van-cell {
+    padding: 0;
+    line-height: 80px;
+    font-size: 30px;
+  }
+}
+.textarea {
+  line-height: 30px !important;
+  margin-top: 25px;
+}
+.but {
+  text-align: center;
+}
+.van-button {
+  width: 690px;
+  margin: 24px auto;
+  color: #fff;
+  background-color: #1677ff;
+  border-radius: 8px;
+  font-size: 30px;
+}
+
+::v-deep .van-field__control {
+  color: #333;
+}
+::v-deep .van-field__control:disabled {
+  color: #333;
+  -webkit-text-fill-color: unset;
+}
+</style>

+ 152 - 0
src/views/weibao/views/followUp/checkNote.vue

@@ -0,0 +1,152 @@
+<template>
+  <div>
+    <van-nav-bar
+      title="处置记录详情"
+      left-arrow
+      fixed
+      placeholder
+      @click-left="$router.back()"
+    />
+
+    <div class="main">
+      <!-- 处置情况 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">处置情况:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.disposalStatus || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 处置措施 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">处置措施:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.disposalMeasures || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 处置时间 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">处置时间:</van-col>
+        <van-col span="18" class="right">{{ showReportDate }}</van-col>
+      </van-row>
+
+      <!-- 责任人 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">责任人:</van-col>
+        <van-col span="18" class="right">{{ detail.employeeName || "-" }}</van-col>
+      </van-row>
+
+      <!-- 处置结果 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">处置结果:</van-col>
+        <van-col span="18" class="right">{{ detail.disposalResult || "-" }}</van-col>
+      </van-row>
+
+      <!-- 原预警级别 仅 预警升降级 显示 -->
+      <van-row v-if="detail.disposalResult === '预警升降级'" class="cell">
+        <van-col span="6" class="left">原预警级别:</van-col>
+        <van-col span="18" class="right">{{ oldWarningText }}</van-col>
+      </van-row>
+
+      <!-- 新预警级别 仅 预警升降级 显示 -->
+      <van-row v-if="detail.disposalResult === '预警升降级'" class="cell">
+        <van-col span="6" class="left">新预警级别:</van-col>
+        <van-col span="18" class="right">{{ newWarningText }}</van-col>
+      </van-row>
+    </div>
+  </div>
+</template>
+
+<script>
+import { warningRecordDetail } from "../../api/index.js";
+
+export default {
+  name: "checkNote",
+  data() {
+    return {
+      id: "",
+      detail: {},
+    };
+  },
+  computed: {
+    // 原预警级别文字转换
+    oldWarningText() {
+      const level = this.$route.query.warningLevel;
+      const map = {
+        1: "蓝色预警",
+        2: "黄色预警",
+        3: "橙色预警",
+        4: "红色预警",
+      };
+      return map[level] || "无";
+    },
+    // 新预警级别文字转换
+    newWarningText() {
+      const level = this.detail.warningLevel;
+      const map = {
+        1: "蓝色预警",
+        2: "黄色预警",
+        3: "橙色预警",
+        4: "红色预警",
+      };
+      return map[level] || "无";
+    },
+    showReportDate() {
+      if (!this.detail.disposalDate) return "-";
+      return this.detail.disposalDate.split(" ")[0];
+    },
+  },
+  // 你要的:created 获取参数 + 请求接口
+  created() {
+    this.id = this.$route.query.id;
+    this.getDetail();
+  },
+  methods: {
+    // 获取详情
+    async getDetail() {
+      try {
+        const res = await warningRecordDetail({ id: this.id });
+        if (res.code === "0000") {
+          this.detail = res.data;
+        }
+      } catch (err) {
+        this.$toast.fail("获取详情失败");
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.main {
+  padding: 0 30px;
+}
+
+/* 核心:支持长文本 + 垂直居中 + 美观对齐 */
+.cell {
+  display: flex;
+  align-items: flex-start;
+  border-bottom: 1px solid #eee;
+  padding: 20px 0;
+  font-size: 30px;
+  line-height: 1.5;
+
+  .left {
+    padding-top: 2px;
+    color: #333;
+    display: flex;
+    align-items: center;
+    min-height: 44px;
+  }
+
+  .right {
+    color: #666;
+    word-break: break-all;
+    white-space: pre-wrap;
+    min-height: 44px;
+    display: flex;
+    align-items: center;
+  }
+}
+</style>

+ 189 - 44
src/views/weibao/views/followUp/details.vue

@@ -1,68 +1,149 @@
 <template>
   <div>
-    <van-nav-bar title="随访记录" @click-left="$router.back()" left-arrow fixed placeholder safe-area-inset-top />
+    <van-nav-bar
+      title="随访记录"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
     <div class="message">
       <van-row class="topBox">
         <van-col span="18">
           <van-row class="row">
-            <van-col span="9">姓名:{{dataList.name}}</van-col>
-            <van-col span="6">性别:{{dataList.sex}}</van-col>
-            <van-col span="9">年龄:{{dataList.age}}</van-col>
+            <van-col span="9">姓名:{{ dataList.name }}</van-col>
+            <van-col span="6">性别:{{ dataList.sex }}</van-col>
+            <van-col span="9">年龄:{{ dataList.age }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">儿童类型:{{dataList.childTypeName}}</van-col>
+            <van-col span="24">儿童类型:{{ dataList.childTypeName }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">教育情况:{{dataList.school}}</van-col>
+            <van-col span="24">教育情况:{{ dataList.school }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">健康情况:{{dataList.healthStatus}}</van-col>
+            <van-col span="24">健康情况:{{ dataList.healthStatus }}</van-col>
           </van-row>
         </van-col>
         <van-col span="6">
-          <img class="tx" :src="dataList.url?dataList.url:require('../../assets/tx.png')" alt="">
+          <img
+            class="tx"
+            :src="dataList.url ? dataList.url : require('../../assets/tx.png')"
+            alt=""
+          />
         </van-col>
         <van-row class="row">
-          <van-col span="10">监护人:{{dataList.guardianName}}</van-col>
-          <van-col span="14">联系方式:{{dataList.guardianPhone}}</van-col>
+          <van-col span="10">监护人:{{ dataList.guardianName }}</van-col>
+          <van-col span="14">联系方式:{{ dataList.guardianPhone }}</van-col>
         </van-row>
         <van-row class="row">
-          <van-col span="24">居住地址:{{dataList.address}}</van-col>
+          <van-col span="24">居住地址:{{ dataList.address }}</van-col>
         </van-row>
       </van-row>
     </div>
+
+    <div class="main" v-if="dataList.warningLevel">
+      <div style="margin-bottom: 20px">
+        <p>预警等级:</p>
+        <div class="box">
+          <div class="record-item" @click="goWarningDetail">
+            <span>{{ warningLevelText }}</span>
+            <van-icon name="arrow" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 处置记录入口 -->
+    <div class="main" v-if="dataList.warningLevel">
+      <div style="margin-bottom: 20px">
+        <p>处置记录:</p>
+        <div class="box">
+          <div
+            v-for="(item, idx) in warningRecordList"
+            :key="idx"
+            class="record-item"
+            @click="goCheckNote(item)"
+          >
+            <span>
+              处置记录 {{ idx + 1 }}:
+              <span style="color: #999; margin-left: 20px">{{
+                item.disposalResult
+              }}</span>
+            </span>
+
+            <van-icon name="arrow" />
+          </div>
+          <div v-if="warningRecordList.length === 0" style="color: #999">
+            暂无处置记录
+          </div>
+        </div>
+      </div>
+    </div>
+
     <div class="main">
       <div>
         <p>随访详情:</p>
         <div class="box">
-          {{dataList.remark}}
+          {{ dataList.remark }}
         </div>
       </div>
       <div>
         <p>照片:</p>
         <div class="boximg">
-          <div v-for="(item,index) in picsArr" :key="index">
-            <van-image lazy-load fit="contain" :src="item" alt="" @click="showPopup(),startPosition=index" />
+          <div v-for="(item, index) in picsArr" :key="index">
+            <van-image
+              lazy-load
+              fit="contain"
+              :src="item"
+              alt=""
+              @click="showPopup(), (startPosition = index)"
+            />
           </div>
         </div>
-        <van-image-preview closeable :startPosition="startPosition" v-model="show" :images="pics"></van-image-preview>
+        <van-image-preview
+          closeable
+          :startPosition="startPosition"
+          v-model="show"
+          :images="pics"
+        ></van-image-preview>
       </div>
       <div>
         <p>视频:</p>
         <div class="boximg">
-          <div v-for="(item,index) in videos" :key="index">
-            <video @click="showvideoplay=true,videourl=item" style="object-fit:cover;" :src="item" poster='../../assets/video.png'></video>
+          <div v-for="(item, index) in videos" :key="index">
+            <video
+              @click="(showvideoplay = true), (videourl = item)"
+              style="object-fit: cover"
+              :src="item"
+              poster="../../assets/video.png"
+            ></video>
           </div>
         </div>
       </div>
     </div>
-    <van-dialog style="width:100%;border-radius:0;height:200px" v-model="showvideoplay" :show-cancel-button="false" :show-confirm-button="false" closeOnClickOverlay>
-      <video controls preload="auto" style="width:100%;height:200px;object-fit: contain;" :src="videourl" v-if="videourl"></video>
+
+    <van-dialog
+      style="width: 100%; border-radius: 0; height: 200px"
+      v-model="showvideoplay"
+      :show-cancel-button="false"
+      :show-confirm-button="false"
+      closeOnClickOverlay
+    >
+      <video
+        controls
+        preload="auto"
+        style="width: 100%; height: 200px; object-fit: contain"
+        :src="videourl"
+        v-if="videourl"
+      ></video>
     </van-dialog>
   </div>
 </template>
+
 <script>
-import { followUpHistoryDetail } from '../../api/index.js'
+import { followUpHistoryDetail, warningRecordList } from "../../api/index.js";
 export default {
   data() {
     return {
@@ -71,41 +152,89 @@ export default {
       showvideoplay: false,
       startPosition: 0,
       index: 0,
-      videourl: '',
+      videourl: "",
       images: [],
-      dataList: '',
-      appendFile: '',
+      dataList: "",
+      appendFile: "",
       pics: [],
       picsArr: [],
       videos: [],
-    }
+      warningRecordList: [], //处置记录列表
+    };
+  },
+  computed: {
+    // 预警等级文字转换
+    warningLevelText() {
+      const level = this.dataList.warningLevel;
+      const map = {
+        1: "蓝色预警",
+        2: "黄色预警",
+        3: "橙色预警",
+        4: "红色预警",
+      };
+      return map[level] || "无";
+    },
   },
   created() {
-    this.id = this.$route.query.id
-    this.followUpHistoryDetail()
+    this.id = this.$route.query.id;
+    this.followUpHistoryDetail();
   },
   methods: {
     async followUpHistoryDetail() {
-      const res = await followUpHistoryDetail({ id: this.id })
-      this.dataList = res.data
-      this.pics = res.data.pics.split(',')
+      const res = await followUpHistoryDetail({ id: this.id });
+      this.dataList = res.data;
+      if (this.dataList.warningId) {
+        this.getWarningRecordList();
+      }
+      this.pics = res.data.pics.split(",");
       this.pics.forEach((item) => {
-        if (item.indexOf('.jpg') || item.indexOf('.png')) {
-          this.picsArr.push(item.replace('/cgpimage/', '/cgpimage/240x240/'))
+        if (item.indexOf(".jpg") || item.indexOf(".png")) {
+          this.picsArr.push(item.replace("/cgpimage/", "/cgpimage/240x240/"));
         }
-      })
-      if (!res.data.videos) return
-      this.videos = res.data.videos.split(',')
+      });
+      if (!res.data.videos) return;
+      this.videos = res.data.videos.split(",");
+    },
+    // 获取处置记录列表
+    async getWarningRecordList() {
+      const res = await warningRecordList({
+        id: this.dataList.warningId,
+        pageNum: 1,
+        pageSize: 30,
+      });
+      if (res.code == "0000") {
+        this.warningRecordList = res.data.records;
+      }
     },
     showPopup() {
-      this.show = true
+      this.show = true;
     },
     onChange(index) {
-      this.index = index
+      this.index = index;
+    },
+    // 跳转到处置详情
+    goCheckNote(item) {
+      this.$router.push({
+        path: "/followUp/checkNote",
+        query: {
+          id: item.id,
+          warningLevel: this.dataList.warningLevel,
+        },
+      });
+    },
+    // 跳转到预警详情
+    goWarningDetail() {
+      this.$router.push({
+        path: "/followUp/warningDetail",
+        query: {
+          id: this.dataList.warningId,
+        },
+      });
     },
   },
-}
+};
 </script>
+
 <style lang="scss" scoped>
 .tx {
   width: 160px;
@@ -132,7 +261,7 @@ export default {
   font-size: 30px;
   text-align: left;
   .box {
-    min-height: 200px;
+    min-height: 100px; /* 适配预警等级单条高度,和处置记录统一 */
     border: 1px solid #eee;
     padding: 20px;
     font-size: 26px;
@@ -146,18 +275,14 @@ export default {
     color: #999;
     display: flex;
     flex-wrap: wrap;
-    // justify-content: space-between;
     ::v-deep .van-image {
       max-width: 200px;
-      //   max-height: 200px;
       margin: 0 10px;
-      //   margin-right: 16px;
       margin-bottom: 16px;
       img {
         max-width: 200px;
         max-height: 200px;
         margin: 0 10px;
-        //   margin-right: 16px;
         margin-bottom: 16px;
       }
     }
@@ -165,12 +290,32 @@ export default {
       width: 180px;
       height: 180px;
       margin: 0 10px;
-      //   margin-right: 16px;
       margin-bottom: 16px;
     }
   }
 }
+
+/* 预警等级 + 处置记录 统一样式 */
+.record-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px 0;
+  font-size: 28px;
+  color: #333;
+  border-bottom: 1px solid #f5f5f5;
+  cursor: pointer;
+  user-select: none;
+  ::v-deep .van-icon {
+    color: #999;
+    font-size: 24px;
+  }
+}
+.record-item:last-child {
+  border-bottom: none;
+}
+
 ::v-deep .van-overlay {
   background-color: rgba(0, 0, 0, 0.75);
 }
-</style>
+</style>

+ 98 - 40
src/views/weibao/views/followUp/director.vue

@@ -45,7 +45,7 @@
     <van-list
       v-model="loading"
       :immediate-check="false"
-      :finished="finished"
+      :finished="active === 0 ? finishedPlan : finishedHistory"
       finished-text="没有更多了"
       @load="onLoad"
     >
@@ -54,21 +54,28 @@
     </van-list>
   </div>
 </template>
+
 <script>
 import { followUpPage, historyFollowUp } from "../../api/index.js";
 import plan from "./plan.vue";
 import history from "./history.vue";
+
 export default {
   components: { plan, history },
   data() {
     return {
-      value: "",
       active: 0,
       loading: false,
-      finished: false,
+
+      // 两个 tab 完全独立状态(核心隔离)
+      finishedPlan: false,
+      finishedHistory: false,
+
       planList: [],
       historyList: [],
+
       roleId: JSON.parse(sessionStorage.getItem("userInfo")).roleIdStr,
+
       followUpform: {
         pageNum: 1,
         pageSize: 20,
@@ -83,85 +90,136 @@ export default {
       },
     };
   },
+
+  // ====================== 页面激活(返回)时执行 ======================
   activated() {
-    this.loading = true;
-    this.followUpPage();
-    this.historyFollowUp();
-    console.log("1");
+    // 只刷新当前 tab
+    this.reloadCurrentTabAndScrollTop();
   },
+
   beforeRouteEnter(to, from, next) {
     next((vm) => {
-      //  这里的vm指的就是vue实例,可以用来当做this使用
-      console.log(to, "to");
-      console.log(from, "from");
-      if (from.name == "Home") {
+      // 从首页进入 → 重置到第一个tab
+      if (from.name === "Home") {
         vm.active = 0;
-        vm.followUpform.pageNum = 1;
-        vm.historyform.pageNum = 1;
       }
     });
   },
+
   methods: {
-    async followUpPage() {
-      const res = await followUpPage(this.followUpform);
-      this.loading = false;
-      //   this.$toast(this.followUpform.pageNum)
-      if (this.followUpform.pageNum == 1) {
-        this.planList = res.data.records;
+    // ====================== 【核心】刷新当前 tab + 清空数据 + 回到第1页 + 滚动顶部 ======================
+    reloadCurrentTabAndScrollTop() {
+      this.loading = true;
+
+      if (this.active === 0) {
+        // 清空计划列表
+        this.planList = [];
+        this.finishedPlan = false;
+        // 回到第1页
+        this.followUpform.pageNum = 1;
+        this.followUpPage();
       } else {
-        this.planList = this.planList.concat(res.data.records);
-        console.log(this.planList);
+        // 清空历史列表
+        this.historyList = [];
+        this.finishedHistory = false;
+        // 回到第1页
+        this.historyform.pageNum = 1;
+        this.historyFollowUp();
       }
-      if (this.planList.length == res.data.total) {
-        this.finished = true;
-      } else {
-        this.finished = false;
+
+      // 自动滚动到页面顶部
+      setTimeout(() => {
+        window.scrollTo(0, 0);
+      }, 100);
+    },
+
+    // ====================== 获取随访计划 ======================
+    async followUpPage() {
+      try {
+        const res = await followUpPage(this.followUpform);
+        if (this.followUpform.pageNum === 1) {
+          this.planList = res.data.records;
+        } else {
+          this.planList = this.planList.concat(res.data.records);
+        }
+        this.finishedPlan = this.planList.length >= res.data.total;
+      } catch (err) {
+        this.$toast.fail("获取计划失败");
+      } finally {
+        this.loading = false;
       }
     },
+
+    // ====================== 获取历史随访 ======================
     async historyFollowUp() {
-      const res = await historyFollowUp(this.historyform);
-      this.loading = false;
-      if (this.historyform.pageNum == 1) {
-        this.historyList = res.data.records;
-      } else {
-        this.historyList = this.historyList.concat(res.data.records);
-      }
-      if (this.historyList.length == res.data.total) {
-        this.finished = true;
-      } else {
-        this.finished = false;
+      try {
+        const res = await historyFollowUp(this.historyform);
+        if (this.historyform.pageNum === 1) {
+          this.historyList = res.data.records;
+        } else {
+          this.historyList = this.historyList.concat(res.data.records);
+        }
+        this.finishedHistory = this.historyList.length >= res.data.total;
+      } catch (err) {
+        this.$toast.fail("获取历史记录失败");
+      } finally {
+        this.loading = false;
       }
     },
+
+    // ====================== 上拉加载更多 ======================
     onLoad() {
-      if (this.active == 0) {
-        this.followUpform.pageNum = this.followUpform.pageNum + 1;
+      if (this.active === 0) {
+        if (this.finishedPlan) {
+          this.loading = false;
+          return;
+        }
+        this.followUpform.pageNum++;
         this.followUpPage();
       } else {
-        this.historyform.pageNum = this.historyform.pageNum + 1;
+        if (this.finishedHistory) {
+          this.loading = false;
+          return;
+        }
+        this.historyform.pageNum++;
         this.historyFollowUp();
       }
     },
+
+    // ====================== tab 切换 ======================
     changeonLoad() {
       this.loading = true;
-      if (this.active == 0) {
+      if (this.active === 0) {
+        this.planList = [];
+        this.finishedPlan = false;
         this.followUpform.pageNum = 1;
         this.followUpPage();
       } else {
+        this.historyList = [];
+        this.finishedHistory = false;
         this.historyform.pageNum = 1;
         this.historyFollowUp();
       }
+      window.scrollTo(0, 0);
     },
+
+    // ====================== 搜索 ======================
     onSearchPlan() {
+      this.planList = [];
+      this.finishedPlan = false;
       this.followUpform.pageNum = 1;
       this.followUpPage();
     },
     onSearch() {
+      this.historyList = [];
+      this.finishedHistory = false;
       this.historyform.pageNum = 1;
       this.historyFollowUp();
     },
   },
 };
 </script>
+
 <style lang="scss" scoped>
 .van-list {
   padding: 0 30px;

+ 1 - 1
src/views/weibao/views/followUp/history.vue

@@ -209,7 +209,7 @@ export default {
     }
     .level-2 {
       background-color: #faad14;
-      color: #1d2129 !important;
+      color: #2b303a !important;
     }
     .level-3 {
       background-color: #fa8c16;

+ 51 - 29
src/views/weibao/views/followUp/historySupervisor.vue

@@ -1,75 +1,97 @@
 <template>
   <div>
-    <van-nav-bar title="历史随访" @click-left="$router.back()" left-arrow fixed placeholder safe-area-inset-top />
+    <van-nav-bar
+      title="历史随访"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
     <div class="box">
-      <van-search v-model="historyform.name" shape="round" show-action placeholder="请输入姓名" @search="onSearch">
+      <van-search
+        v-model="historyform.name"
+        shape="round"
+        show-action
+        placeholder="请输入姓名"
+        @search="onSearch"
+      >
         <template #action>
-          <div style="display:flex;align-items:center;">
+          <div style="display: flex; align-items: center">
             <div @click="onSearch">搜索</div>
-            <van-button v-if="roleIdStr==15" size="mini" @click="$router.push({path:'/followUp/addfollow'})">新建随访</van-button>
+            <van-button
+              v-if="roleIdStr == 15"
+              size="mini"
+              @click="$router.push({ path: '/followUp/addfollowwarn' })"
+              >新建随访</van-button
+            >
           </div>
         </template>
       </van-search>
     </div>
-    <van-list v-model="loading" :immediate-check="false" :finished="finished" finished-text="没有更多了" @load="onLoad">
+    <van-list
+      v-model="loading"
+      :immediate-check="false"
+      :finished="finished"
+      finished-text="没有更多了"
+      @load="onLoad"
+    >
       <history :historyList="historyList"></history>
     </van-list>
   </div>
 </template>
 <script>
-import { historyFollowUp } from '../../api/index.js'
-import history from './history.vue'
+import { historyFollowUp } from "../../api/index.js";
+import history from "./history.vue";
 export default {
   components: { history },
   data() {
     return {
-      roleIdStr: JSON.parse(sessionStorage.getItem('userInfo')).roleIdStr,
+      roleIdStr: JSON.parse(sessionStorage.getItem("userInfo")).roleIdStr,
       active: 0,
       loading: false,
       finished: false,
-      historyList: '',
+      historyList: "",
       historyform: {
         pageNum: 1,
         pageSize: 20,
         isSupervise:
-          JSON.parse(sessionStorage.getItem('userInfo')).roleIdStr == 14
-            ? 2
-            : 1,
-        name: '',
+          JSON.parse(sessionStorage.getItem("userInfo")).roleIdStr == 14 ? 2 : 1,
+        name: "",
       },
-    }
+    };
   },
   created() {
-    this.loading = true
-    this.historyFollowUp()
+    this.loading = true;
+    this.historyFollowUp();
   },
   methods: {
     async historyFollowUp() {
-      const res = await historyFollowUp(this.historyform)
-      this.loading = false
+      const res = await historyFollowUp(this.historyform);
+      this.loading = false;
       if (this.historyform.pageNum == 1) {
-        this.historyList = res.data.records
+        this.historyList = res.data.records;
       } else {
-        this.historyList = this.historyList.concat(res.data.records)
+        this.historyList = this.historyList.concat(res.data.records);
       }
 
       if (this.historyList.length >= res.data.total) {
-        this.finished = true
+        this.finished = true;
       } else {
-        this.finished = false
+        this.finished = false;
       }
     },
     onLoad() {
-      this.loading = true
-      this.historyform.pageNum = this.historyform.pageNum + 1
-      this.historyFollowUp()
+      this.loading = true;
+      this.historyform.pageNum = this.historyform.pageNum + 1;
+      this.historyFollowUp();
     },
     onSearch() {
-      this.historyform.pageNum = 1
-      this.historyFollowUp()
+      this.historyform.pageNum = 1;
+      this.historyFollowUp();
     },
   },
-}
+};
 </script>
 <style lang="scss" scoped>
 .box {
@@ -94,4 +116,4 @@ export default {
 .van-search__action:active {
   background-color: transparent;
 }
-</style>
+</style>

+ 4 - 2
src/views/weibao/views/followUp/plan.vue

@@ -43,7 +43,9 @@
           </div>
           <van-button
             size="mini"
-            @click="$router.push({ path: '/followUp/record', query: { id: item.id } })"
+            @click="
+              $router.push({ path: '/followUp/record', query: { childId: item.id } })
+            "
             >开始随访</van-button
           >
         </div>
@@ -195,7 +197,7 @@ export default {
     }
     .level-2 {
       background-color: #faad14; // 更暖的黄色
-      color: #1d2129 !important;
+      color: #2b303a !important;
     }
     .level-3 {
       background-color: #fa8c16; // 更柔和的橙色

+ 356 - 107
src/views/weibao/views/followUp/record.vue

@@ -1,33 +1,44 @@
 <template>
   <div>
-    <van-nav-bar title="随访记录" @click-left="$router.back()" left-arrow fixed placeholder safe-area-inset-top />
+    <van-nav-bar
+      title="随访记录"
+      @click-left="$router.back()"
+      left-arrow
+      fixed
+      placeholder
+      safe-area-inset-top
+    />
     <div class="message">
       <van-row class="topBox">
         <van-col span="18">
           <van-row class="row">
-            <van-col span="9">姓名:{{childInfo.name}}</van-col>
-            <van-col span="6">性别:{{childInfo.sex}}</van-col>
-            <van-col span="9">年龄:{{childInfo.age}}</van-col>
+            <van-col span="9">姓名:{{ childInfo.name }}</van-col>
+            <van-col span="6">性别:{{ childInfo.sex }}</van-col>
+            <van-col span="9">年龄:{{ childInfo.age }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">儿童类型:{{childInfo.childTypeName}}</van-col>
+            <van-col span="24">儿童类型:{{ childInfo.childTypeName }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">教育情况:{{childInfo.school}}</van-col>
+            <van-col span="24">教育情况:{{ childInfo.school }}</van-col>
           </van-row>
           <van-row class="row">
-            <van-col span="24">健康情况:{{childInfo.healthStatus}}</van-col>
+            <van-col span="24">健康情况:{{ childInfo.healthStatus }}</van-col>
           </van-row>
         </van-col>
         <van-col span="6">
-          <img class="tx" :src="childInfo.url?childInfo.url:require('../../assets/tx.png')" alt="">
+          <img
+            class="tx"
+            :src="childInfo.url ? childInfo.url : require('../../assets/tx.png')"
+            alt=""
+          />
         </van-col>
         <van-row class="row">
-          <van-col span="10">监护人:{{childInfo.guardianName}}</van-col>
-          <van-col span="14">联系方式:{{childInfo.guardianPhone}}</van-col>
+          <van-col span="10">监护人:{{ childInfo.guardianName }}</van-col>
+          <van-col span="14">联系方式:{{ childInfo.guardianPhone }}</van-col>
         </van-row>
         <van-row class="row">
-          <van-col span="24">居住地址:{{childInfo.address}}</van-col>
+          <van-col span="24">居住地址:{{ childInfo.address }}</van-col>
         </van-row>
       </van-row>
     </div>
@@ -35,190 +46,432 @@
       <div class="main">
         <van-row class="cell">
           <van-col span="6">随访时间:</van-col>
-          <!-- <van-col span="18" @click="showPopup">{{form.followDate?form.followDate:'请选择'}}</van-col> -->
           <van-col span="18">
-            <van-field v-model="form.followDate" @click="showPopup" :rules="[{ required: true}]" placeholder="请选择" readonly />
+            <van-field
+              v-model="form.followDate"
+              @click="showPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+            />
           </van-col>
           <van-popup v-model="show" position="bottom" :style="{ height: '50%' }">
-            <van-datetime-picker @confirm="dateConfirm" @cancel="showPopup" v-model="followDate" type="date" :min-date="minDate" :max-date="maxDate" :columns-order="[ 'year','month', 'day']"
-              :formatter="formatter" />
+            <van-datetime-picker
+              @confirm="dateConfirm"
+              @cancel="showPopup"
+              v-model="followDate"
+              type="date"
+              :min-date="minDate"
+              :max-date="maxDate"
+              :columns-order="['year', 'month', 'day']"
+              :formatter="formatter"
+            />
           </van-popup>
         </van-row>
         <van-row class="cell">
           <van-col span="6">选择街道:</van-col>
-          <!-- <van-col span="18" @click="streetshowPopup">{{form.streetName?form.streetName:'请选择'}}</van-col> -->
           <van-col span="18">
-            <van-field v-model="form.streetName" @click="streetshowPopup" :rules="[{ required: true}]" placeholder="请选择" readonly />
+            <van-field
+              v-model="form.streetName"
+              @click="streetshowPopup"
+              :rules="[{ required: true }]"
+              placeholder="请选择"
+              readonly
+            />
           </van-col>
           <van-popup v-model="streetShow" position="bottom" :style="{ height: '50%' }">
-            <van-picker default-index="2" :visible-item-count="6" show-toolbar :columns="streetList" @confirm="onConfirm" @cancel="streetshowPopup" @change="onChange" value-key="name" />
+            <van-picker
+              default-index="2"
+              :visible-item-count="6"
+              show-toolbar
+              :columns="streetList"
+              @confirm="onConfirm"
+              @cancel="streetshowPopup"
+              @change="onChange"
+              value-key="name"
+            />
           </van-popup>
         </van-row>
         <van-row class="cell">
           <van-col span="6">随访地点:</van-col>
           <van-col span="18">
-            <van-field v-model="form.place" :rules="[{ required: true}]" placeholder="请输入随访地点" />
+            <van-field
+              v-model="form.place"
+              :rules="[{ required: true }]"
+              placeholder="请输入随访地点"
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
           <van-col span="6">随访人员:</van-col>
           <van-col span="18">
-            <van-field v-model="form.personnel" :rules="[{ required: true}]" placeholder="请输入随访人员" />
+            <van-field
+              v-model="form.personnel"
+              :rules="[{ required: true }]"
+              placeholder="请输入随访人员"
+            />
           </van-col>
         </van-row>
         <van-row class="cell">
           <van-col span="6">联系方式:</van-col>
           <van-col span="18">
-            <van-field v-model="form.phone" type="tel" :rules="[{validator,required: true, message:'手机号格式不正确'}]" placeholder="请输入" />
+            <van-field
+              v-model="form.phone"
+              type="tel"
+              :rules="[{ validator, required: true, message: '手机号格式不正确' }]"
+              placeholder="请输入"
+            />
           </van-col>
         </van-row>
+        <!-- 随访记录 -->
         <van-row class="cell">
           <van-col span="6">随访记录:</van-col>
           <van-col span="18">
-            <van-field class="textarea" :rules="[{ required: true}]" v-model="form.remark" rows="1" type="textarea" placeholder="请输入随访记录" />
+            <van-field
+              class="textarea"
+              :rules="[{ required: true }]"
+              v-model="form.remark"
+              rows="1"
+              type="textarea"
+              placeholder="请输入随访记录"
+            />
+          </van-col>
+        </van-row>
+
+        <!--预警风险描述 -->
+        <van-row v-if="!childInfo.warningId" class="cell">
+          <van-col span="6">预警风险:</van-col>
+          <van-col span="18">
+            <van-field
+              placeholder="请选择预警风险"
+              :value="riskText"
+              readonly
+              is-link
+              @click="openWarningSheet"
+            />
+          </van-col>
+        </van-row>
+        <vanUploader
+          ref="imgUploader"
+          :text="'图片'"
+          @getfileList="getfileList"
+        ></vanUploader>
+        <vanUploader
+          ref="videoUploader"
+          :text="'视频'"
+          @getfileList="getfileList"
+        ></vanUploader>
+
+        <van-row v-if="childInfo.warningLevel" class="cell">
+          <van-col span="6">预警等级:</van-col>
+          <van-col span="18">
+            <van-field
+              :value="warningText"
+              readonly
+              is-link
+              @click="checkWarningDetail()"
+            />
+          </van-col>
+        </van-row>
+
+        <van-row v-for="(item, index) in warningRecordList" :key="item.id" class="cell">
+          <van-col span="6">查看处置{{ index + 1 }}:</van-col>
+          <van-col span="18">
+            <van-field
+              :value="item.disposalResult"
+              readonly
+              is-link
+              @click="checkNoteHandle(item)"
+            />
+          </van-col>
+        </van-row>
+
+        <van-row v-if="childInfo.warningId && isAddRecord" class="cell">
+          <van-col span="6">添加处置:</van-col>
+          <van-col span="18">
+            <van-field
+              placeholder="请添加处置记录"
+              readonly
+              is-link
+              @click="addNoteHandle"
+            />
           </van-col>
         </van-row>
-        <vanUploader :text="'图片'" @getfileList="getfileList"></vanUploader>
-        <vanUploader :text="'视频'" @getfileList="getfileList"></vanUploader>
       </div>
+
       <div class="but">
         <van-button native-type="submit" :disabled="disabled">确 定</van-button>
       </div>
     </van-form>
+
+    <!-- 预警选择弹窗组件 -->
+    <WarningSheet
+      v-model="showWarningSheet"
+      :list="warningList"
+      :selected-value="riskValue"
+      @confirm="handleWarningConfirm"
+    >
+    </WarningSheet>
   </div>
 </template>
+
 <script>
-import { saveFollowUp, getStreetList, getChildInfo } from '../../api/index.js'
-import vanUploader from '../../components/vanUploader.vue'
+import {
+  saveFollowUp,
+  getStreetList,
+  getChildInfo,
+  warningReasons,
+  warningSave,
+  warningRecordList,
+} from "../../api/index.js";
+import WarningSheet from "../../components/warningSheet.vue";
+import vanUploader from "../../components/vanUploader.vue";
+
 export default {
-  components: { vanUploader },
+  components: { vanUploader, WarningSheet },
   data() {
     return {
       show: false,
       streetShow: false,
       disabled: false,
       showvideoplay: false,
-      videourl: '',
+      videourl: "",
       fileList: [],
-      form: {
-        fileList: [],
-      },
-      streetList: '',
+      form: { fileList: [] },
+      streetList: "",
       followDate: new Date(),
       minDate: new Date(2020, 0, 1),
       maxDate: new Date(),
-      customFieldName: {
-        text: 'name',
-        id: 'id',
-      },
-      childInfo: '',
+      customFieldName: { text: "name", id: "id" },
+      childInfo: "",
       pics: [],
       videos: [],
+
+      showWarningSheet: false,
+      riskText: "",
+      warningList: [],
+      riskValue: "",
+      riskLabel: "",
+
+      isAddRecord: false,
+      warningRecordList: [],
+    };
+  },
+
+  // 监听路由:只有【新进入】才刷新
+  beforeRouteEnter(to, from, next) {
+    console.log("路由");
+    console.log(to);
+    console.log(from);
+    next((vm) => {
+      // 只有从 /home 进来或在本界面手动刷新,才刷新页面!
+      if (from.path === "/followUp/director" || from.path === "/") {
+        vm.reloadPageData();
+      }
+    });
+  },
+
+  // 离开页面时:如果是回到 /home,清空所有状态!
+  beforeRouteLeave(to, from, next) {
+    if (to.path === "/followUp/director") {
+      // 清空关键标记,保证下次从home进入一定刷新
+      this.childInfo = null;
+      this.lastEnteredFromHome = false;
     }
+    next();
   },
-  created() {
-    this.id = this.$route.query.id
-    this.getStreetList()
-    this.getChildInfo()
+
+  computed: {
+    warningText() {
+      const level = this.childInfo?.warningLevel;
+      const map = { 1: "蓝色预警", 2: "黄色预警", 3: "橙色预警", 4: "红色预警" };
+      return map[level] || "";
+    },
   },
+
+  created() {},
+
+  // 返回页面:只刷新处置记录,不清空表单!
+  activated() {
+    if (this.childInfo?.warningId) {
+      this.getWarningRecordList();
+    }
+  },
+
   methods: {
+    // ===================== 完全保留你原版的重置方法 =====================
+    async reloadPageData() {
+      // 先清空上传照片和视频的组件
+      if (this.$refs.imgUploader) this.$refs.imgUploader.childrenClear();
+      if (this.$refs.videoUploader) this.$refs.videoUploader.childrenClear();
+      this.childInfo = "";
+      this.pics = [];
+      this.videos = [];
+      this.warningRecordList = [];
+      this.form = { fileList: [] };
+      this.riskText = "";
+      this.isAddRecord = false;
+      this.disabled = false;
+
+      await this.getChildInfo();
+      this.getWarningReasons();
+      this.getStreetList();
+    },
+
+    openWarningSheet() {
+      this.showWarningSheet = true;
+    },
+    async handleWarningConfirm({ item, levelText }) {
+      try {
+        const res = await warningSave({
+          childId: this.form.childId,
+          warningReason: item.label,
+        });
+        if (res.code === "0000") {
+          this.$toast.success("保存成功");
+          this.showWarningSheet = false;
+          this.childInfo.warningId = res.data.id;
+          this.childInfo.warningLevel = res.data.warningLevel;
+          this.riskText = levelText;
+          this.getWarningRecordList();
+        }
+      } catch (err) {
+        this.$toast.fail("保存失败");
+      }
+    },
+    checkWarningDetail() {
+      this.$router.push({
+        path: "/followUp/warningDetail",
+        query: { id: this.childInfo.warningId },
+      });
+    },
+    addNoteHandle() {
+      this.$router.push({
+        path: "/followUp/addNote",
+        query: {
+          warningId: this.childInfo.warningId,
+          warningLevel: this.childInfo.warningLevel,
+        },
+      });
+    },
+    checkNoteHandle(e) {
+      this.$router.push({
+        path: "/followUp/checkNote",
+        query: { id: e.id, warningLevel: this.childInfo.warningLevel },
+      });
+    },
+
     validator(val) {
-      return /^1(3|4|5|6|7|8|9)\d{9}$/.test(val)
+      return /^1(3|4|5|6|7|8|9)\d{9}$/.test(val);
     },
     async getChildInfo() {
-      const res = await getChildInfo({ id: this.id })
-      this.childInfo = res.data
-      this.form.childId = res.data.childId
+      const res = await getChildInfo({ id: this.$route.query.childId });
+      this.childInfo = res.data;
+      this.form.childId = res.data.childId;
+      this.getWarningRecordList();
     },
     formatter(type, val) {
-      if (type === 'year') {
-        return val + '年'
-      }
-      if (type === 'month') {
-        return val + '月'
-      }
-      if (type === 'day') {
-        return val + '日'
-      }
-      return val
+      if (type === "year") return val + "年";
+      if (type === "month") return val + "月";
+      if (type === "day") return val + "日";
+      return val;
     },
     timeFormat(time) {
-      // 时间格式化 2019-09-08
-      let year = time.getFullYear()
-      let month = time.getMonth() + 1
-      if (month < 10) {
-        month = '0' + month
-      }
-
-      let day = time.getDate()
-      if (day < 10) {
-        day = '0' + day
-      }
-      return year + '-' + month + '-' + day
+      let year = time.getFullYear();
+      let month = time.getMonth() + 1;
+      let day = time.getDate();
+      if (month < 10) month = "0" + month;
+      if (day < 10) day = "0" + day;
+      return year + "-" + month + "-" + day;
     },
     showPopup() {
-      this.show = !this.show
+      this.show = !this.show;
     },
     streetshowPopup() {
-      this.streetShow = !this.streetShow
+      this.streetShow = !this.streetShow;
     },
     onChange() {},
     onConfirm(value) {
-      this.form.streetName = value.name
-      this.form.areaId = value.id
-      this.streetShow = false
+      this.form.streetName = value.name;
+      this.form.areaId = value.id;
+      this.streetShow = false;
     },
     dateConfirm(v) {
-      console.log(v)
-      this.form.followDate = this.timeFormat(v)
-      this.show = false
+      this.form.followDate = this.timeFormat(v);
+      this.show = false;
     },
     async saveFollowUp() {
       if (this.pics.length == 0) {
-        this.$toast.fail('请选择至少一张图片')
-        return
+        this.$toast.fail("请选择至少一张图片");
+        return;
       }
-      this.form.pics = this.pics.join(',')
-      this.form.videos = this.videos.join(',')
-      var data = JSON.parse(JSON.stringify(this.form))
-      data.followDate = data.followDate + ' 00:00:00'
-      const res = await saveFollowUp(data)
-      if (res.code == '0000') {
-        this.disabled = true
-        this.$toast.success('成功')
-        this.$router.go(-1)
+      this.form.pics = this.pics.join(",");
+      this.form.videos = this.videos.join(",");
+      this.form.warningId = this.childInfo.warningId;
+      this.form.warningLevel = this.childInfo.warningLevel;
+      var data = JSON.parse(JSON.stringify(this.form));
+      data.followDate = data.followDate + " 00:00:00";
+      const res = await saveFollowUp(data);
+      if (res.code == "0000") {
+        this.disabled = true;
+        this.$toast.success("成功");
+        this.$router.go(-1);
       }
     },
     async getStreetList() {
-      const res = await getStreetList()
-      this.streetList = res.data
+      const res = await getStreetList();
+      this.streetList = res.data;
     },
     getfileList(v, index) {
       if (index == undefined) {
         if (
-          v.indexOf('.png') != -1 ||
-          v.indexOf('.jpg') != -1 ||
-          v.indexOf('.jpeg') != -1
-        ) {
-          this.pics.push(v)
-        } else {
-          this.videos.push(v)
-        }
+          v.indexOf(".png") != -1 ||
+          v.indexOf(".jpg") != -1 ||
+          v.indexOf(".jpeg") != -1
+        )
+          this.pics.push(v);
+        else this.videos.push(v);
       } else {
-        if (v == '图片') {
-          this.pics.splice(index, 1)
-        } else {
-          this.videos.splice(index, 1)
-        }
+        v == "图片" ? this.pics.splice(index, 1) : this.videos.splice(index, 1);
+      }
+    },
+    async getWarningRecordList() {
+      const res = await warningRecordList({
+        id: this.childInfo.warningId,
+        pageNum: 1,
+        pageSize: 30,
+      });
+      if (res.code == "0000") {
+        this.warningRecordList = res.data.records;
+        this.checkIsAddRecord();
       }
     },
+    async getWarningReasons() {
+      const res = await warningReasons();
+      let arr = [];
+      for (let key in res.data) arr.push({ value: key, label: res.data[key] });
+      this.warningList = arr;
+    },
+    checkIsAddRecord() {
+      if (!this.warningRecordList || this.warningRecordList.length === 0) {
+        this.isAddRecord = true;
+        return;
+      }
+      const temp = this.warningRecordList.some(
+        (item) => item.disposalResult === "风险解除"
+      );
+      this.isAddRecord = !temp;
+    },
   },
-}
+};
 </script>
+
 <style lang="scss" scoped>
 .tx {
   width: 160px;
   height: 160px;
+  object-fit: cover;
+  border-radius: 8px;
 }
 .message {
   padding: 0 30px;
@@ -279,7 +532,6 @@ export default {
       height: 200px;
       margin: 0 20px;
     }
-
     img {
       width: 100%;
       height: 100%;
@@ -287,7 +539,6 @@ export default {
     video {
       width: 100%;
     }
-    // background-color: chocolate;
   }
   .van-uploader {
     position: absolute;
@@ -299,7 +550,6 @@ export default {
         height: 200px;
       }
     }
-
     ::v-deep .van-uploader__preview {
       margin: 0 20px;
       .van-uploader__file {
@@ -309,7 +559,6 @@ export default {
         width: 200px;
         height: 200px;
         opacity: 0;
-        // margin: 20px;
       }
       width: 200px;
       height: 200px;
@@ -323,4 +572,4 @@ export default {
   color: #333;
   -webkit-text-fill-color: unset;
 }
-</style>
+</style>

+ 152 - 0
src/views/weibao/views/followUp/warningDetail.vue

@@ -0,0 +1,152 @@
+<template>
+  <div>
+    <van-nav-bar
+      title="预警记录详情"
+      left-arrow
+      fixed
+      placeholder
+      @click-left="$router.back()"
+    />
+
+    <div class="main">
+      <!-- 儿童名称 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">儿童名称:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.childName || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 预警事由 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">预警事由:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.warningReason || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 预警级别 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">预警级别:</van-col>
+        <van-col span="18" class="right">
+          {{ warningLevelText }}
+        </van-col>
+      </van-row>
+
+      <!-- 上报情况 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">上报情况:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.reportDetail || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 上报平台 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">上报平台:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.reportPlatform || "-" }}
+        </van-col>
+      </van-row>
+
+      <!-- 上报时间 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">上报时间:</van-col>
+        <van-col span="18" class="right">
+          {{ showReportDate }}
+        </van-col>
+      </van-row>
+
+      <!-- 备注 -->
+      <van-row class="cell">
+        <van-col span="6" class="left">备注:</van-col>
+        <van-col span="18" class="right">
+          {{ detail.remark || "-" }}
+        </van-col>
+      </van-row>
+    </div>
+  </div>
+</template>
+
+<script>
+// 引入接口
+import { warningDetail } from "../../api/index.js";
+
+export default {
+  name: "warningDetail",
+  data() {
+    return {
+      id: "",
+      detail: {},
+    };
+  },
+  computed: {
+    // 预警级别 1-4 转文字
+    warningLevelText() {
+      const level = this.detail.warningLevel;
+      const map = {
+        1: "蓝色预警",
+        2: "黄色预警",
+        3: "橙色预警",
+        4: "红色预警",
+      };
+      return map[level] || "未设置";
+    },
+
+    showReportDate() {
+      if (!this.detail.reportDate) return "-";
+      return this.detail.reportDate.split(" ")[0];
+    },
+  },
+  created() {
+    // 获取上一页传入的 id
+    this.id = this.$route.query.id;
+    this.getDetail();
+  },
+  methods: {
+    // 获取预警详情接口
+    async getDetail() {
+      try {
+        const res = await warningDetail({ id: this.id });
+        if (res.code === "0000") {
+          this.detail = res.data;
+        }
+      } catch (err) {
+        this.$toast.fail("获取详情失败");
+      }
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.main {
+  padding: 0 30px;
+}
+
+/* 统一美观样式 + 支持长文本自动换行 */
+.cell {
+  display: flex;
+  align-items: flex-start;
+  border-bottom: 1px solid #eee;
+  padding: 20px 0;
+  font-size: 30px;
+  line-height: 1.5;
+
+  .left {
+    color: #333;
+    min-height: 44px;
+    display: flex;
+    align-items: center;
+  }
+
+  .right {
+    color: #666;
+    word-break: break-all;
+    white-space: pre-wrap;
+    min-height: 44px;
+    display: flex;
+    align-items: center;
+  }
+}
+</style>