From 2d4a6ebe75599eaa51d5fb0f522dc9fa6825af00 Mon Sep 17 00:00:00 2001
From: wupengfei <834520024@qq.com>
Date: 星期三, 12 二月 2025 14:58:29 +0800
Subject: [PATCH] feat: mine

---
 apps/cMiniApp/src/components/Portal/portal-host.vue                    |  104 ++++++
 apps/cMiniApp/src/components/Portal/PortalManager.vue                  |   44 ++
 apps/cMiniApp/src/assets/mine/icon-collect.png                         |    0 
 apps/cMiniApp/src/subpackages/mine/editMineInfo/InnerPage.vue          |   65 ++++
 apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.config.ts |    3 
 apps/cMiniApp/src/components/Input/input.ts                            |   31 ++
 apps/cMiniApp/src/components/Input/ChooseInput.vue                     |   37 ++
 apps/cMiniApp/src/components/Portal/Portal.vue                         |   19 +
 apps/cMiniApp/src/assets/mine/icon-agreement.png                       |    0 
 apps/cMiniApp/src/components/Input/ChooseInputWithAreaPicker.vue       |   76 +++++
 apps/cMiniApp/src/assets/mine/icon-recruit.png                         |    0 
 apps/cMiniApp/src/components/Portal/portal-container.vue               |   50 +++
 apps/cMiniApp/src/components/Portal/portal-consumer.vue                |   37 ++
 apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.vue       |   17 +
 apps/cMiniApp/src/subpackages/mine/editMineInfo/constants/index.ts     |   19 +
 apps/cMiniApp/src/constants/router.ts                                  |    1 
 apps/cMiniApp/src/app.config.ts                                        |    1 
 apps/cMiniApp/src/components/Input/NumberInput.vue                     |   55 +++
 apps/cMiniApp/src/components/Portal/index.ts                           |   12 
 apps/cMiniApp/src/components/Portal/portal.ts                          |   72 ++++
 apps/cMiniApp/src/subpackages/mine/mineCurriculumVitae/InnerPage.vue   |  139 ++++++++
 apps/cMiniApp/src/pages/mine/index.vue                                 |   16 
 apps/cMiniApp/src/components/Input/ChooseInputWithPicker.vue           |   73 ++++
 23 files changed, 857 insertions(+), 14 deletions(-)

diff --git a/apps/cMiniApp/src/app.config.ts b/apps/cMiniApp/src/app.config.ts
index 72d0941..2816e7a 100644
--- a/apps/cMiniApp/src/app.config.ts
+++ b/apps/cMiniApp/src/app.config.ts
@@ -80,6 +80,7 @@
         'mineAgreementSign/mineAgreementSign',
         'mineAgreementSignDetail/mineAgreementSignDetail',
         'mineCurriculumVitae/mineCurriculumVitae',
+        'editMineInfo/editMineInfo',
         'setting/setting',
       ],
     },
diff --git a/apps/cMiniApp/src/assets/mine/icon-agreement.png b/apps/cMiniApp/src/assets/mine/icon-agreement.png
new file mode 100644
index 0000000..1cd2914
--- /dev/null
+++ b/apps/cMiniApp/src/assets/mine/icon-agreement.png
Binary files differ
diff --git a/apps/cMiniApp/src/assets/mine/icon-collect.png b/apps/cMiniApp/src/assets/mine/icon-collect.png
new file mode 100644
index 0000000..7f3b0e3
--- /dev/null
+++ b/apps/cMiniApp/src/assets/mine/icon-collect.png
Binary files differ
diff --git a/apps/cMiniApp/src/assets/mine/icon-recruit.png b/apps/cMiniApp/src/assets/mine/icon-recruit.png
new file mode 100644
index 0000000..98107c1
--- /dev/null
+++ b/apps/cMiniApp/src/assets/mine/icon-recruit.png
Binary files differ
diff --git a/apps/cMiniApp/src/components/Input/ChooseInput.vue b/apps/cMiniApp/src/components/Input/ChooseInput.vue
new file mode 100644
index 0000000..6486c46
--- /dev/null
+++ b/apps/cMiniApp/src/components/Input/ChooseInput.vue
@@ -0,0 +1,37 @@
+<template>
+  <nut-input
+    class="nut-input-text bole-input-text"
+    type="text"
+    readonly
+    alwaysEmbed
+    v-bind="$attrs"
+  >
+    <template #clear>
+      <slot name="clear"></slot>
+    </template>
+    <template #right>
+      <slot name="right">
+        <RectRight :size="12" class="common-choose-input-icon" />
+      </slot>
+    </template>
+  </nut-input>
+</template>
+
+<script setup lang="ts">
+import { RectRight } from '@nutui/icons-vue-taro';
+
+defineOptions({
+  name: 'ChooseInput',
+});
+</script>
+
+<style lang="scss">
+@import '@/styles/common.scss';
+
+.common-choose-input-icon {
+  //   width: 13px;
+  //   height: 23px;
+  margin-left: 18px;
+  color: boleGetCssVar('text-color', 'primary');
+}
+</style>
diff --git a/apps/cMiniApp/src/components/Input/ChooseInputWithAreaPicker.vue b/apps/cMiniApp/src/components/Input/ChooseInputWithAreaPicker.vue
new file mode 100644
index 0000000..b7eaec8
--- /dev/null
+++ b/apps/cMiniApp/src/components/Input/ChooseInputWithAreaPicker.vue
@@ -0,0 +1,76 @@
+<template>
+  <ChooseInput :modelValue="inputValue" @click="handleOpen()"></ChooseInput>
+</template>
+
+<script setup lang="ts">
+import { useAllAreaList } from '@/hooks';
+import Portal from '../Portal';
+import ChooseInput from './ChooseInput.vue';
+import { Popup, Picker } from '@nutui/nutui-taro';
+
+defineOptions({
+  name: 'ChooseInputWithAreaPicker',
+});
+
+const { findAreaNameFromCode } = useAllAreaList();
+
+type Props = {
+  fieldNames?: object;
+  columns: API.AreaTreeNode[];
+  modelValue: Array<string | number>;
+  title?: string;
+};
+
+const props = withDefaults(defineProps<Props>(), {
+  title: '閫夋嫨鍦板潃',
+  fieldNames: () => ({
+    text: 'areaName',
+    value: 'areaCode',
+    children: 'children',
+  }),
+});
+const inputValue = computed(() =>
+  props.modelValue.map((x) => findAreaNameFromCode(Number(x))).join(',')
+);
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', val: Array<string | number>): void;
+}>();
+
+function handleOpen() {
+  Portal.add((key) => {
+    return h(
+      Portal.Container,
+      { keyNumber: key, delayOpen: true },
+      {
+        default: ({ open, onClose }) =>
+          h(
+            Popup,
+            {
+              visible: open.value,
+              'onUpdate:visible': (value) => !value && onClose(),
+              position: 'bottom',
+            },
+            {
+              default: () =>
+                h(Picker, {
+                  modelValue: props.modelValue,
+                  columns: props.columns,
+                  fieldNames: props.fieldNames,
+                  title: props.title,
+                  onCancel: onClose,
+                  onConfirm: ({ selectedValue, selectedOptions }) => {
+                    emit(
+                      'update:modelValue',
+                      selectedOptions.map((x) => x.areaCode)
+                    );
+                    onClose();
+                  },
+                }),
+            }
+          ),
+      }
+    );
+  });
+}
+</script>
diff --git a/apps/cMiniApp/src/components/Input/ChooseInputWithPicker.vue b/apps/cMiniApp/src/components/Input/ChooseInputWithPicker.vue
new file mode 100644
index 0000000..dacca73
--- /dev/null
+++ b/apps/cMiniApp/src/components/Input/ChooseInputWithPicker.vue
@@ -0,0 +1,73 @@
+<template>
+  <ChooseInput :modelValue="inputValue" @click="handleOpen()"></ChooseInput>
+</template>
+
+<script setup lang="ts">
+import Portal from '../Portal';
+import ChooseInput from './ChooseInput.vue';
+import { Popup, Picker } from '@nutui/nutui-taro';
+import { convertOptions, ValueEnum } from './input';
+
+defineOptions({
+  name: 'ChooseInputWithPicker',
+});
+
+type Props = {
+  enumLabelKey?: string;
+  enumValueKey?: string;
+  valueEnum?: ValueEnum;
+  modelValue: string | number;
+};
+
+const props = withDefaults(defineProps<Props>(), {
+  enumLabelKey: 'name',
+  enumValueKey: 'id',
+});
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', val: string | number): void;
+}>();
+
+const options = computed(() =>
+  convertOptions(props.valueEnum, props.enumLabelKey, props.enumValueKey)
+);
+
+const inputValue = computed(
+  () => options.value?.find((x) => x.value === props.modelValue)?.text ?? ''
+);
+
+function handleOpen() {
+  console.log('handleOpen: ');
+  const _modelValue = [props.modelValue];
+  Portal.add((key) => {
+    return h(
+      Portal.Container,
+      { keyNumber: key, delayOpen: true },
+      {
+        default: ({ open, onClose }) =>
+          h(
+            Popup,
+            {
+              visible: open.value,
+              'onUpdate:visible': (value) => !value && onClose(),
+              position: 'bottom',
+            },
+            {
+              default: () =>
+                h(Picker, {
+                  modelValue: _modelValue,
+                  columns: options.value,
+                  onCancel: onClose,
+                  onConfirm: ({ selectedValue, selectedOptions }) => {
+                    console.log('selectedValue: ', selectedValue, selectedOptions);
+                    emit('update:modelValue', selectedOptions[0].value);
+                    onClose();
+                  },
+                }),
+            }
+          ),
+      }
+    );
+  });
+}
+</script>
diff --git a/apps/cMiniApp/src/components/Input/NumberInput.vue b/apps/cMiniApp/src/components/Input/NumberInput.vue
new file mode 100644
index 0000000..05ba733
--- /dev/null
+++ b/apps/cMiniApp/src/components/Input/NumberInput.vue
@@ -0,0 +1,55 @@
+<template>
+  <nut-input type="number" :formatter="formatter" formatTrigger="onBlur">
+    <template #right>
+      <slot name="right"></slot>
+    </template>
+  </nut-input>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+  name: 'NumberInput',
+});
+
+type Props = {
+  min?: number;
+  max?: number;
+  precision?: number;
+};
+
+const props = withDefaults(defineProps<Props>(), {
+  max: Math.pow(2, 53) - 1,
+});
+
+function formatter(value: string): string {
+  const newVal = value !== '' ? Number.parseFloat(value) : '';
+  if (Number.isNaN(newVal)) {
+    return '';
+  }
+  if (newVal && newVal > props.max) {
+    return `${toPrecision(props.max)}`;
+  }
+  if (props.min !== undefined && newVal && newVal < props.min) {
+    return `${toPrecision(props.min)}`;
+  }
+  return newVal !== '' ? `${toPrecision(newVal)}` : newVal;
+}
+
+function toPrecision(num: number) {
+  if (props.precision) {
+    if (props.precision === 0) return Math.round(num);
+    let snum = String(num);
+    const pointPos = snum.indexOf('.');
+    if (pointPos === -1) return num;
+    const nums = snum.replace('.', '').split('');
+    const datum = nums[pointPos + props.precision];
+    if (!datum) return num;
+    const length = snum.length;
+    if (snum.charAt(length - 1) === '5') {
+      snum = `${snum.slice(0, Math.max(0, length - 1))}6`;
+    }
+    return Number(snum).toFixed(props.precision);
+  }
+  return String(num);
+}
+</script>
diff --git a/apps/cMiniApp/src/components/Input/input.ts b/apps/cMiniApp/src/components/Input/input.ts
new file mode 100644
index 0000000..d1a5309
--- /dev/null
+++ b/apps/cMiniApp/src/components/Input/input.ts
@@ -0,0 +1,31 @@
+import _ from 'lodash';
+
+export type ValueEnumItem = { [key: string]: any };
+export type ValueEnum = ValueEnumItem[] | Record<string | number, string>;
+export type OptionItem = {
+  text: string;
+  value: string | number;
+  [key: string]: any;
+};
+
+export function convertOptions(
+  valueEnum: ValueEnum,
+  enumLabelKey: string,
+  enumValueKey: string
+): OptionItem[] {
+  return _.isArray(valueEnum)
+    ? valueEnum.map((x) => ({
+        ...x,
+        text: x[enumLabelKey],
+        value: _.isNaN(Number(x[enumValueKey])) ? x[enumValueKey] : Number(x[enumValueKey]),
+      }))
+    : Object.keys(valueEnum).map((x) => ({
+        value: _.isNaN(Number(x)) ? x : Number(x),
+        text: valueEnum[x],
+      }));
+}
+
+export type ChooseCheckBoxOptionItem = {
+  text: string;
+  value: string | number;
+};
diff --git a/apps/cMiniApp/src/components/Portal/Portal.vue b/apps/cMiniApp/src/components/Portal/Portal.vue
new file mode 100644
index 0000000..06d5c1a
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/Portal.vue
@@ -0,0 +1,19 @@
+<template>
+  <PortalConsumer :manager="portalContext">
+    <template #default>
+      <slot />
+    </template>
+  </PortalConsumer>
+</template>
+
+<script setup lang="ts">
+import PortalConsumer from './portal-consumer.vue';
+// import PortalContainer from './portal-container';
+import { portal, usePortalContext } from './portal';
+
+defineOptions({
+  name: 'Portal',
+});
+
+const portalContext = usePortalContext();
+</script>
diff --git a/apps/cMiniApp/src/components/Portal/PortalManager.vue b/apps/cMiniApp/src/components/Portal/PortalManager.vue
new file mode 100644
index 0000000..4f0a6cd
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/PortalManager.vue
@@ -0,0 +1,44 @@
+<script lang="ts">
+import { State, PortalNode } from './portal';
+
+export default defineComponent({
+  name: 'PortalManager',
+  setup(props, { expose }) {
+    const state = reactive<State>({
+      portals: [],
+    });
+
+    const mount = (key: number, children: PortalNode) => {
+      state.portals.push({ key, children });
+    };
+    const update = (key: number, children: PortalNode) => {
+      state.portals = state.portals.map((item) => {
+        if (item.key === key) {
+          return { ...item, children };
+        }
+        return item;
+      });
+    };
+
+    const unmount = (key: number) => {
+      state.portals = state.portals.filter((item) => item.key !== key);
+    };
+
+    expose({
+      mount,
+      update,
+      unmount,
+    });
+
+    return () => {
+      return h(
+        'div',
+        null,
+        state.portals.map((item) => {
+          return h('div', { key: item.key }, [item.children]);
+        })
+      );
+    };
+  },
+});
+</script>
diff --git a/apps/cMiniApp/src/components/Portal/index.ts b/apps/cMiniApp/src/components/Portal/index.ts
new file mode 100644
index 0000000..29fbcf8
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/index.ts
@@ -0,0 +1,12 @@
+import PortalContainer from './portal-container.vue';
+import PortalHost from './portal-host.vue';
+import { portal } from './portal';
+import Portal from './Portal.vue';
+
+export default {
+  Host: PortalHost,
+  add: portal.add,
+  remove: portal.remove,
+  Portal: Portal,
+  Container: PortalContainer,
+};
diff --git a/apps/cMiniApp/src/components/Portal/portal-consumer.vue b/apps/cMiniApp/src/components/Portal/portal-consumer.vue
new file mode 100644
index 0000000..ba1a582
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/portal-consumer.vue
@@ -0,0 +1,37 @@
+<script lang="ts">
+import { PortalMethods } from './portal';
+
+export default defineComponent({
+  name: 'PortalConsumer',
+  props: {
+    manager: {
+      type: Object as PropType<PortalMethods>,
+    },
+  },
+  setup(props) {
+    const _key = ref<number>();
+
+    const slots = useSlots();
+
+    onMounted(() => {
+      if (!props.manager) {
+        throw new Error('forgot');
+      }
+      const defaultSlot = slots.default();
+      props.manager.update(_key.value, defaultSlot);
+      _key.value = props.manager.mount(defaultSlot);
+    });
+
+    onUpdated(() => {
+      const defaultSlot = slots.default();
+      props.manager.update(_key.value, defaultSlot);
+    });
+
+    onUnmounted(() => {
+      props.manager.unmount(_key.value);
+    });
+
+    return () => null;
+  },
+});
+</script>
diff --git a/apps/cMiniApp/src/components/Portal/portal-container.vue b/apps/cMiniApp/src/components/Portal/portal-container.vue
new file mode 100644
index 0000000..7c12a7e
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/portal-container.vue
@@ -0,0 +1,50 @@
+<script lang="ts">
+import { VNode } from 'vue';
+import { portal } from './portal';
+
+export default defineComponent({
+  name: 'portal-container',
+  props: {
+    render: {
+      type: Function as PropType<(open: Ref<boolean>, onClose?: () => any) => VNode>,
+    },
+    keyNumber: {
+      type: Number,
+    },
+    /**
+     * 鏄惁寤惰繜璁剧疆open涓簍rue
+     */
+    delayOpen: {
+      type: Boolean,
+    },
+  },
+  setup(props, { slots }) {
+    const open = ref(!props.delayOpen);
+
+    function onClose() {
+      open.value = false;
+    }
+
+    onMounted(() => {
+      if (props.delayOpen) {
+        setTimeout(() => {
+          open.value = true;
+        }, 30);
+      }
+    });
+
+    watch(open, (open, preOpen) => {
+      if (preOpen && !open) {
+        setTimeout(() => {
+          portal.remove(props.keyNumber);
+        }, 300);
+      }
+    });
+
+    return () => {
+      //   const childrenNode = props.render(open, onClose);
+      return slots.default?.({ open, onClose });
+    };
+  },
+});
+</script>
diff --git a/apps/cMiniApp/src/components/Portal/portal-host.vue b/apps/cMiniApp/src/components/Portal/portal-host.vue
new file mode 100644
index 0000000..05068a7
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/portal-host.vue
@@ -0,0 +1,104 @@
+<template>
+  <slot></slot>
+  <PortalManager ref="_manager" />
+</template>
+
+<script lang="ts">
+defineComponent({
+  name: 'portal-host',
+  inheritAttrs: false,
+});
+</script>
+
+<script setup lang="ts">
+import {
+  PortalContextKey,
+  Operation,
+  getUniqueKey,
+  addType,
+  removeType,
+  TopViewEventEmitter,
+  PortalNode,
+  PortalManagerInstance,
+} from './portal';
+import PortalManager from './PortalManager.vue';
+
+const _nextKey = ref(0);
+const _queue = ref<Operation[]>([]);
+const _manager = ref<PortalManagerInstance>();
+
+onMounted(() => {
+  TopViewEventEmitter.on(getUniqueKey(addType), _mount);
+  TopViewEventEmitter.on(getUniqueKey(removeType), _unmount);
+
+  while (_queue.value.length && _manager.value) {
+    const action = _queue.value.pop();
+    if (!action) {
+      continue;
+    }
+
+    switch (action.type) {
+      case 'mount':
+        _manager.value.mount(action.key, action.children);
+        break;
+      case 'update':
+        _manager.value.update(action.key, action.children);
+        break;
+      case 'unmount':
+        _manager.value.unmount(action.key);
+        break;
+    }
+  }
+});
+
+onUnmounted(() => {
+  TopViewEventEmitter.off(getUniqueKey(addType), _mount);
+  TopViewEventEmitter.off(getUniqueKey(removeType), _unmount);
+});
+
+const _setManager = (manager?: any) => {
+  _manager.value = manager;
+};
+
+const _mount = (children: PortalNode, _key?: number) => {
+  const key = _key || _nextKey.value++;
+  if (_manager.value) {
+    _manager.value.mount(key, children);
+  } else {
+    _queue.value.push({ type: 'mount', key, children });
+  }
+
+  return key;
+};
+
+const _update = (key: number, children: PortalNode) => {
+  if (_manager.value) {
+    _manager.value.update(key, children);
+  } else {
+    const op: Operation = { type: 'mount', key, children };
+    const index = _queue.value.findIndex(
+      (o) => o.type === 'mount' || (o.type === 'update' && o.key === key)
+    );
+
+    if (index > -1) {
+      _queue.value[index] = op;
+    } else {
+      _queue.value.push(op);
+    }
+  }
+};
+
+const _unmount = (key: number) => {
+  if (_manager.value) {
+    _manager.value.unmount(key);
+  } else {
+    _queue.value.push({ type: 'unmount', key });
+  }
+};
+
+provide(PortalContextKey, {
+  mount: _mount,
+  update: _update,
+  unmount: _unmount,
+});
+</script>
diff --git a/apps/cMiniApp/src/components/Portal/portal.ts b/apps/cMiniApp/src/components/Portal/portal.ts
new file mode 100644
index 0000000..38b8216
--- /dev/null
+++ b/apps/cMiniApp/src/components/Portal/portal.ts
@@ -0,0 +1,72 @@
+import type { VNode } from 'vue';
+import Taro from '@tarojs/taro';
+
+export type PortalNode = VNode | VNode[];
+
+export type State = {
+  portals: Array<{
+    key: number;
+    children: PortalNode;
+  }>;
+};
+export type PortalManagerState = {
+  portals: Array<{
+    key: number;
+    children: PortalNode;
+  }>;
+};
+
+export type PortalMethods = {
+  mount: (children: PortalNode) => number;
+  update: (key: number, children: PortalNode) => void;
+  unmount: (key: number) => void;
+};
+
+export type Operation =
+  | { type: 'mount'; key: number; children: PortalNode }
+  | { type: 'update'; key: number; children: PortalNode }
+  | { type: 'unmount'; key: number };
+
+export const PortalContextKey: InjectionKey<PortalMethods> = Symbol('PortalContextKey');
+
+export function usePortalContext() {
+  return inject(PortalContextKey);
+}
+
+export function getUniqueKey(key: string) {
+  const router = Taro.getCurrentInstance().router;
+  const prefix = router.path;
+  return `${prefix}_${key}`;
+}
+
+// events
+export const addType = 'ADD_PORTAL';
+export const removeType = 'REMOVE_PORTAL';
+
+export const TopViewEventEmitter = new Taro.Events();
+
+class PortalGuard {
+  private nextKey = 10000;
+  add = (render: (_key: number) => PortalNode) => {
+    const key = this.nextKey++;
+    const node = render(key);
+    TopViewEventEmitter.trigger(getUniqueKey(addType), node, key);
+    return key;
+  };
+  remove = (key: number) => {
+    TopViewEventEmitter.trigger(getUniqueKey(removeType), key);
+  };
+}
+/**
+ * portal
+ */
+export const portal = new PortalGuard();
+
+export type PortalManagerInstance = {
+  mount: (key: number, children: PortalNode) => void;
+  update: (key: number, children: PortalNode) => void;
+  unmount: (key: number) => {
+    key: number;
+    children: PortalNode;
+  }[];
+};
diff --git a/apps/cMiniApp/src/constants/router.ts b/apps/cMiniApp/src/constants/router.ts
index 98d8b95..083e742 100644
--- a/apps/cMiniApp/src/constants/router.ts
+++ b/apps/cMiniApp/src/constants/router.ts
@@ -33,5 +33,6 @@
   mineAgreementSign = '/subpackages/mine/mineAgreementSign/mineAgreementSign',
   mineAgreementSignDetail = '/subpackages/mine/mineAgreementSignDetail/mineAgreementSignDetail',
   mineCurriculumVitae = '/subpackages/mine/mineCurriculumVitae/mineCurriculumVitae',
+  editMineInfo = '/subpackages/mine/editMineInfo/editMineInfo',
   setting = '/subpackages/mine/setting/setting',
 }
diff --git a/apps/cMiniApp/src/pages/mine/index.vue b/apps/cMiniApp/src/pages/mine/index.vue
index 5e04d6a..e7ecf54 100644
--- a/apps/cMiniApp/src/pages/mine/index.vue
+++ b/apps/cMiniApp/src/pages/mine/index.vue
@@ -75,10 +75,10 @@
         </div>
       </List>
       <List class="mine-list-wrapper mine-setting-list">
-        <ListItem :icon="IconSetting" title="鎴戞敹钘忕殑浠诲姟" @click="goMineCollectTask"></ListItem>
-        <ListItem :icon="IconSetting" title="鍗忚绛剧害" @click="goMineAgreementSign"></ListItem>
+        <ListItem :icon="IconCollect" title="鎴戞敹钘忕殑浠诲姟" @click="goMineCollectTask"></ListItem>
+        <ListItem :icon="IconAgreement" title="鍗忚绛剧害" @click="goMineAgreementSign"></ListItem>
         <ListItem :icon="IconSetting" title="璁剧疆" @click="goSetting"></ListItem>
-        <ListItem :icon="IconSetting" title="鎴戣鎷涗汉/鐢ㄤ汉" @click="goSetting"></ListItem>
+        <ListItem :icon="IconRecruit" title="鎴戣鎷涗汉/鐢ㄤ汉" @click="goSetting"></ListItem>
       </List>
     </ContentScrollView>
   </PageLayoutWithBg>
@@ -88,6 +88,9 @@
 import { TransparentNavigationBar, ContentScrollView, UserHomeTopView } from '@/components';
 import IconArrow from '@/assets/setting/icon-arrow.png';
 import IconSetting from '@/assets/mine/icon-setting.png';
+import IconCollect from '@/assets/mine/icon-collect.png';
+import IconAgreement from '@/assets/mine/icon-agreement.png';
+import IconRecruit from '@/assets/mine/icon-recruit.png';
 import IconOrderSign from '@/assets/mine/icon-order-sign.png';
 import IconOrderHire from '@/assets/mine/icon-order-hire.png';
 import IconOrderCancel from '@/assets/mine/icon-order-cancel.png';
@@ -162,4 +165,11 @@
 .mine-setting-badge {
   margin-right: 20px;
 }
+
+.mine-setting-list {
+  .pro-list-item-icon {
+    width: 48px;
+    height: 48px;
+  }
+}
 </style>
diff --git a/apps/cMiniApp/src/subpackages/mine/editMineInfo/InnerPage.vue b/apps/cMiniApp/src/subpackages/mine/editMineInfo/InnerPage.vue
new file mode 100644
index 0000000..b3e507b
--- /dev/null
+++ b/apps/cMiniApp/src/subpackages/mine/editMineInfo/InnerPage.vue
@@ -0,0 +1,65 @@
+<template>
+  <ContentScrollView :paddingH="false">
+    <nut-form :model-value="form" ref="formRef" :rules="rules">
+      <nut-form-item label="濮撳悕:" prop="name">
+        <nut-input v-model.trim="form.name" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" />
+      </nut-form-item>
+      <nut-form-item label="鎵嬫満鍙�:" prop="phone" required>
+        <nut-input v-model.trim="form.phone" placeholder="璇疯緭鍏ユ墜鏈哄彿" type="text" />
+      </nut-form-item>
+      <nut-form-item label="韬唤:" class="bole-form-item" prop="gender">
+        <ChooseInputWithPicker
+          v-model="form.gender"
+          placeholder="璇烽�夋嫨韬唤"
+          :value-enum="MineHireTypeText"
+        />
+      </nut-form-item>
+    </nut-form>
+  </ContentScrollView>
+  <PageFooter>
+    <PageFooterBtn type="primary" @click="handleConfirm">淇濆瓨</PageFooterBtn>
+  </PageFooter>
+</template>
+
+<script setup lang="ts">
+import { useUserStore } from '@/stores/modules/user';
+import { FormRules } from '@nutui/nutui-taro/dist/types/__VUE/form/types';
+import ChooseInputWithPicker from '@/components/Input/ChooseInputWithPicker.vue';
+import { MineHireTypeText, MineHireType } from './constants';
+
+defineOptions({
+  name: 'InnerPage',
+});
+
+const userStore = useUserStore();
+
+const form = reactive({
+  name: '',
+  phone: '',
+  gender: MineHireType.All,
+});
+
+const rules = reactive<FormRules>({
+  phone: [{ required: true, message: '璇疯緭鍏ユ墜鏈哄彿' }],
+});
+const formRef = ref<any>(null);
+function handleConfirm() {
+  if (!formRef.value) return;
+  formRef.value.validate().then(({ valid, errors }: any) => {
+    if (valid) {
+      confirm();
+    }
+  });
+}
+
+function confirm() {}
+</script>
+
+<style lang="scss">
+@import '@/styles/common.scss';
+
+.task-card-actions-text {
+  font-size: 24px;
+  line-height: 42px;
+}
+</style>
diff --git a/apps/cMiniApp/src/subpackages/mine/editMineInfo/constants/index.ts b/apps/cMiniApp/src/subpackages/mine/editMineInfo/constants/index.ts
new file mode 100644
index 0000000..c3d377a
--- /dev/null
+++ b/apps/cMiniApp/src/subpackages/mine/editMineInfo/constants/index.ts
@@ -0,0 +1,19 @@
+export enum MineHireType {
+  All = 0,
+  InProgress = 10,
+  WaitCheck = 20,
+  Finished = 30,
+}
+
+export const MineHireTypeText = {
+  [MineHireType.All]: '鍏ㄩ儴',
+  [MineHireType.InProgress]: '杩涜涓�',
+  [MineHireType.WaitCheck]: '寰呴獙鏀�',
+  [MineHireType.Finished]: '宸插畬鎴�',
+};
+
+export const MineHireTypeColor = {
+  [MineHireType.InProgress]: '#3A71FF',
+  [MineHireType.WaitCheck]: '#FF7D00',
+  [MineHireType.Finished]: '#9FA4AC',
+};
diff --git a/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.config.ts b/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.config.ts
new file mode 100644
index 0000000..305fdb1
--- /dev/null
+++ b/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.config.ts
@@ -0,0 +1,3 @@
+export default definePageConfig({
+  disableScroll: true,
+});
diff --git a/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.vue b/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.vue
new file mode 100644
index 0000000..8485302
--- /dev/null
+++ b/apps/cMiniApp/src/subpackages/mine/editMineInfo/editMineInfo.vue
@@ -0,0 +1,17 @@
+<template>
+  <PageLayout class="index-page-wrapper" :title="'缂栬緫涓汉淇℃伅'">
+    <InnerPage></InnerPage>
+  </PageLayout>
+</template>
+
+<script setup lang="ts">
+import InnerPage from './InnerPage.vue';
+
+defineOptions({
+  name: 'editMineInfo',
+});
+</script>
+
+<style lang="scss">
+@import '@/styles/common.scss';
+</style>
diff --git a/apps/cMiniApp/src/subpackages/mine/mineCurriculumVitae/InnerPage.vue b/apps/cMiniApp/src/subpackages/mine/mineCurriculumVitae/InnerPage.vue
index 61d5f17..f0d25e7 100644
--- a/apps/cMiniApp/src/subpackages/mine/mineCurriculumVitae/InnerPage.vue
+++ b/apps/cMiniApp/src/subpackages/mine/mineCurriculumVitae/InnerPage.vue
@@ -19,7 +19,7 @@
               <img :src="IconAuth" class="mine-curriculum-info-item-gender-icon" />
             </div>
           </div>
-          <IconFont name="rect-right" size="16" color="#6D6E6E"></IconFont>
+          <IconFont name="rect-right" size="16" color="#6D6E6E" @click="goEditMineInfo"></IconFont>
         </div>
         <div class="mine-curriculum-info-item">
           <div class="mine-curriculum-info-item-left">
@@ -35,28 +35,64 @@
       </div>
     </div>
     <List class="mine-curriculum-intention">
-      <div class="mine-curriculum-intention-title">
-        <div class="mine-curriculum-intention-title-text">姹傝亴鎰忓悜</div>
-        <div class="mine-curriculum-intention-title-edit">
-          缂栬緫
-          <img :src="IconArrow" class="" />
+      <div class="mine-curriculum-intention-content">
+        <div class="mine-curriculum-intention-title">
+          <div class="mine-curriculum-intention-title-text">姹傝亴鎰忓悜</div>
+          <div class="mine-curriculum-intention-title-edit">
+            缂栬緫
+            <img :src="IconArrow" class="mine-curriculum-intention-title-edit-icon" />
+          </div>
         </div>
+        <MineAgreementSignDetailItem label="鏈熸湜宀椾綅" class="mine-curriculum-intention-job">
+          <template #detail>
+            <div class="mine-curriculum-intention-job-content">
+              <div class="mine-curriculum-intention-job-item">瀹㈡埧鏈嶅姟鍛�</div>
+              <div class="mine-curriculum-intention-job-item">瀹㈡埧鏈嶅姟鍛�</div>
+              <div class="mine-curriculum-intention-job-item">瀹㈡埧鏈嶅姟鍛�</div>
+              <div class="mine-curriculum-intention-job-item">瀹㈡埧鏈嶅姟鍛�</div>
+            </div>
+          </template>
+        </MineAgreementSignDetailItem>
+        <MineAgreementSignDetailItem label="绌洪棽鏃堕棿">
+          <template #detail>
+            <div class="mine-curriculum-intention-bold">涓嶉檺</div>
+          </template>
+        </MineAgreementSignDetailItem>
+        <MineAgreementSignDetailItem label="姹傝亴鐘舵��">
+          <template #detail>
+            <div class="mine-curriculum-intention-bold">绉瀬鎵惧伐浣�</div>
+          </template>
+        </MineAgreementSignDetailItem>
       </div>
+
+      <ListItem title="璧勬牸璇佷功">
+        <template #extra>
+          <div class="mine-curriculum-intention-enit-btn">缂栬緫</div>
+        </template>
+      </ListItem>
+      <ListItem title="宸ヤ綔缁忛獙">
+        <template #extra>
+          <div class="mine-curriculum-intention-enit-btn">缂栬緫</div>
+        </template>
+      </ListItem>
+      <ListItem title="璇︾粏淇℃伅" class="mine-curriculum-intention-detail">
+        <template #extra>
+          <div class="mine-curriculum-intention-enit-btn">缂栬緫</div>
+        </template>
+      </ListItem>
     </List>
-    <ListItem title="璧勬牸璇佷功">
-      <template #extra>
-        <div class="user-account">缂栬緫</div>
-      </template>
-    </ListItem>
   </ContentView>
 </template>
 
 <script setup lang="ts">
 import { useUserStore } from '@/stores/modules/user';
 import { useIsLogin } from '@/hooks';
+import MineAgreementSignDetailItem from '../mineAgreementSignDetail/MineAgreementSignDetailItem.vue';
 import { List, ListItem } from '@12333/components';
+import { RouterPath } from '@/constants';
 import IconArrow from '@/assets/setting/icon-arrow.png';
 import IconAuth from '@/assets/mine/icon-auth.png';
+import Taro from '@tarojs/taro';
 
 defineOptions({
   name: 'InnerPage',
@@ -64,6 +100,12 @@
 
 const userStore = useUserStore();
 const isLogin = useIsLogin();
+
+function goEditMineInfo() {
+  Taro.navigateTo({
+    url: RouterPath.editMineInfo,
+  });
+}
 </script>
 
 <style lang="scss">
@@ -97,6 +139,7 @@
         .mine-curriculum-info-item-name {
           font-size: 32px;
           line-height: 40px;
+          font-weight: 600;
           color: boleGetCssVar('text-color', 'primary');
         }
 
@@ -118,6 +161,7 @@
         .mine-curriculum-info-item-phone {
           font-size: 28px;
           line-height: 40px;
+          font-weight: 400;
           color: boleGetCssVar('text-color', 'primary');
         }
 
@@ -135,4 +179,77 @@
     }
   }
 }
+
+.mine-curriculum-intention {
+  padding: 30px 0;
+  border-radius: 12px;
+
+  .mine-curriculum-intention-content {
+    padding: 0 boleGetCssVar('size', 'body-padding-h') 40px;
+    border-bottom: 1px solid #f6f6f6;
+
+    .mine-curriculum-intention-title {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 40px;
+
+      .mine-curriculum-intention-title-text {
+        font-size: 32px;
+        font-weight: 600;
+        color: boleGetCssVar('text-color', 'primary');
+      }
+
+      .mine-curriculum-intention-title-edit {
+        display: inline-flex;
+        align-items: center;
+        font-size: 22px;
+        color: boleGetCssVar('text-color', 'regular');
+
+        .mine-curriculum-intention-title-edit-icon {
+          width: 32px;
+          height: 32px;
+        }
+      }
+    }
+
+    .mine-curriculum-intention-job {
+      align-items: flex-start;
+
+      .mine-curriculum-intention-job-content {
+        display: inline-flex;
+        flex-wrap: wrap;
+
+        .mine-curriculum-intention-job-item {
+          font-size: 20px;
+          color: boleGetCssVar('color', 'primary');
+          padding: 6px 16px;
+          background-color: #edf2ff;
+          border-radius: 4px;
+          display: inline-flex;
+          align-items: center;
+          justify-content: center;
+          margin-right: 16px;
+          margin-bottom: 14px;
+        }
+      }
+    }
+
+    .mine-curriculum-intention-bold {
+      font-weight: 600;
+      font-size: 28px;
+    }
+  }
+
+  .mine-curriculum-intention-enit-btn {
+    font-size: 22px;
+    color: boleGetCssVar('text-color', 'regular');
+  }
+
+  .mine-curriculum-intention-detail {
+    .pro-list-item-inner {
+      border-bottom: none;
+    }
+  }
+}
 </style>

--
Gitblit v1.9.1