Ver código fonte

优化表单4

快乐的梦鱼 2 meses atrás
pai
commit
d10e6ae40f

+ 4 - 4
src/api/inhert/VillageInfoApi.ts

@@ -93,10 +93,10 @@ export class VillageListItem extends DataModel<VillageListItem> {
     this._afterSolveServer = () => {
       if (!this.title) {
         if (this.name) this.title = this.name as string;
-        if (typeof this.content === 'object' && (this.content as any)?.title) this.title = (this.content as any).title as string;
-        if (this.content) this.title = this.content as string;
-        if (this.structure) this.title = this.structure as string;
-        if (this.wisdom) this.title = this.wisdom as string;
+        else if (typeof this.content === 'object' && (this.content as any)?.title) this.title = (this.content as any).title as string;
+        else if (this.content) this.title = this.content as string;
+        else if (this.structure) this.title = this.structure as string;
+        else if (this.wisdom) this.title = this.wisdom as string;
       }
       if (!this.image) {
         if (this.distribution) this.image = this.distribution as string;

+ 6 - 3
src/components/basic/ActivityIndicator.vue

@@ -3,11 +3,14 @@
     class="nana-activity-indicator"
     :style="style"
   >
-    <!-- #ifndef APP-NVUE -->
+    <!-- #ifndef APP-NVUE || MP -->
     <svg class="chrome-spinner" viewBox="0 0 50 50">
       <circle cx="25" cy="25" r="20" class="single-ring" :stroke-width="props.strokeWidth" />
     </svg>
     <!-- #endif -->
+    <!-- #ifdef APP-NVUE || MP -->
+    
+    <!-- #endif -->
   </view>
 </template>
 
@@ -53,7 +56,7 @@ const style = computed(() => {
 </script>
 
 <style lang="scss">
-/* #ifndef APP-NVUE */
+/* #ifndef APP-NVUE || MP */
 
 /* 旋转动画 */
 @keyframes spin {
@@ -100,7 +103,7 @@ const style = computed(() => {
 }
 /* #endif */
 
-/* #ifdef APP-NVUE */
+/* #ifdef APP-NVUE || MP */
 @keyframes spin {
   from {
     transform: rotate(0deg);

+ 1 - 1
src/components/dynamic/wrappers/PickerCityField.vue

@@ -6,7 +6,7 @@
       :modelValue="modelValue"
       @update:modelValue="$emit('update:modelValue', $event)"
       textKey="name"
-      :valueKey="stringValue ? 'name' : 'code'"1
+      :valueKey="stringValue ? 'name' : 'code'"
       childrenKey="children"
       placeholder="请选择省市区" 
       :data="(ChinaCityData.data.value as CascaderItem[]) || []"

+ 2 - 3
src/components/dynamic/wrappers/RadioIdField.vue

@@ -9,13 +9,12 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, watch, onMounted } from 'vue';
 import RadioValue from './RadioValue.vue';
-import type { RadioIdFieldOption, RadioIdFieldProps } from './RadioIdField';
 import { useDataLoader } from '@/components/composeabe/DataLoader';
+import type { RadioIdFieldOption, RadioIdFieldProps } from './RadioIdField';
 
 const props = defineProps<RadioIdFieldProps & {
-  modelValue: number|string,
+  modelValue?: number|string,
 }>();
 const loader = useDataLoader<RadioIdFieldOption[]>(async () => await props.loadData(), {
   immediate: true,

+ 4 - 23
src/components/dynamic/wrappers/RadioValue.vue

@@ -1,8 +1,8 @@
 <template>
   <FlexView :direction="vertical ? 'column' : 'row'" align="center" :gap="10" wrap>
     <RadioGroup
-      :value="selectValue"
-      @update:value="onUpdateValue"
+      :modelValue="modelValue"
+      @update:modelValue="emits('update:modelValue', $event)"
       :disabled="disabled"
       v-bind="$attrs"
     >
@@ -46,31 +46,12 @@ const props = defineProps({
     default: null,
   },
   modelValue: {
+    type: [String, Number],
+    default: null,
   },
 });
 
 const emits = defineEmits([
   'update:modelValue',
 ]);
-
-const selectValue = ref<string|null>('');
-
-function setRadioValue() {
-  const options = props.options || [];
-  selectValue.value = options.find(k => (k.value === props.modelValue))?.text || null;
-  if (selectValue.value === null)
-    selectValue.value = options.find(k => (typeof k.value === typeof props.modelValue))?.text || null;
-}
-
-watch(() => props.modelValue, () => {
-  setRadioValue();
-});
-onMounted(() => {
-  setRadioValue();
-});
-
-function onUpdateValue(v : unknown) {
-  emits('update:modelValue', props.options.find(k => k.text === v)?.value);
-}
-
 </script>

+ 8 - 16
src/components/form/Cascader.vue

@@ -39,6 +39,7 @@ import SimpleList from '../list/SimpleList.vue';
 import { DynamicSize } from '../theme/ThemeTools';
 import LoadingPage from '../display/loading/LoadingPage.vue';
 import Empty from '../feedback/Empty.vue';
+import { getCascaderText } from './CascaderUtils';
 
 const themeContext = useTheme();
 
@@ -185,22 +186,13 @@ const currentCheckedItem = computed(() => {
 });
 
 function updateText(value: (number|string)[]) {
-  const selectTexts : string[] = [];
-  let currentGroup : CascaderItem[]|undefined = props.data;
-  let i = 0;
-  for (; i < props.modelValue.length; i++) {
-    const item : CascaderItem|undefined = currentGroup?.find(item => item[props.valueKey] === value[i]);
-    if (item) {
-      selectTexts.push(item[props.textKey]);
-      currentGroup = item[props.childrenKey];
-    }
-  }
-  if (currentGroup !== undefined) {
-    const item : CascaderItem|undefined = currentGroup?.find(item => item[props.valueKey] === value[i]);
-    if (item)
-      selectTexts.push(item[props.textKey]);
-  }
-  emit('selectTextChange', selectTexts.join(' / '));
+  emit('selectTextChange', getCascaderText(
+    value, 
+    props.valueKey, 
+    props.textKey, 
+    props.childrenKey, 
+    props.data
+  ));
 }
 async function loadAsyncData(group: CascaderItem) {
   if (props.asyncLoadData) {

+ 17 - 2
src/components/form/CascaderField.vue

@@ -34,15 +34,16 @@
 </template>
 
 <script setup lang="ts">
-import { ref, toRef } from 'vue';
+import { nextTick, onMounted, ref, toRef, watch } from 'vue';
 import { useFieldChildValueInjector } from './FormContext';
 import { usePickerFieldTempStorageData } from './PickerUtils';
-import type { CascaderProps } from './Cascader.vue';
+import type { CascaderItem, CascaderProps } from './Cascader.vue';
 import Popup from '../dialog/Popup.vue';
 import Cascader from './Cascader.vue';
 import Height from '../layout/space/Height.vue';
 import Text, { type TextProps } from '../basic/Text.vue';
 import PopupTitle from '../dialog/PopupTitle.vue';
+import { getCascaderText } from './CascaderUtils';
 
 export interface CascaderFieldProps extends Omit<CascaderProps, 'modelValue'> {
   
@@ -92,6 +93,9 @@ const props = withDefaults(defineProps<CascaderFieldProps>(), {
   }),
   showSelectText: true,
   autoConfirm: true,
+  textKey: 'text',
+  valueKey: 'value',
+  childrenKey: 'children',
 });
 
 const popupShow = ref(false);
@@ -130,6 +134,17 @@ function onPickEnd() {
     onConfirm();
 }
 
+watch(tempValue, (v) => {
+  if (!popupShow.value)
+    onSelectTextChange(getCascaderText(
+      v, 
+      props.valueKey, 
+      props.textKey, 
+      props.childrenKey, 
+      props.data
+    ), true);
+}, { immediate: true })
+
 defineExpose({
   confirm: onConfirm,
   cancel: onCancel,

+ 26 - 0
src/components/form/CascaderUtils.ts

@@ -0,0 +1,26 @@
+import type { CascaderItem } from "./Cascader.vue";
+
+export function getCascaderText(
+  value: (number|string)[], 
+  valueKey: string, 
+  textKey: string, 
+  childrenKey: string,
+  data: CascaderItem[],
+) {
+  const selectTexts : string[] = [];
+  let currentGroup : CascaderItem[]|undefined = data;
+  let i = 0;
+  for (; i < value.length; i++) {
+    const item : CascaderItem|undefined = currentGroup?.find(item => item[valueKey] === value[i]);
+    if (item) {
+      selectTexts.push(item[textKey]);
+      currentGroup = item[childrenKey];
+    }
+  }
+  if (currentGroup !== undefined) {
+    const item : CascaderItem|undefined = currentGroup?.find(item => item[valueKey] === value[i]);
+    if (item)
+      selectTexts.push(item[textKey]);
+  }
+  return selectTexts.join(' / ');
+}

+ 6 - 2
src/components/form/PickerUtils.ts

@@ -14,9 +14,11 @@ export function usePickerFieldTempStorageData<T>(
   const tempValue = ref(value.value ?? defaultNewValue) as Ref<T>;
   const selectText = ref('');
 
-  function onSelectTextChange(t: string) {
+  function onSelectTextChange(t: string, forceUpdate = false) {
     tempSelectText = t;
     emit('selectTextChange', t);
+    if (forceUpdate)
+      selectText.value = t;
   }
   function onCancel() {
     closePopup();
@@ -31,7 +33,9 @@ export function usePickerFieldTempStorageData<T>(
     emit('confirm', value.value);
   }
 
-  watch(value, (v) => tempValue.value = v);
+  watch(value, (v) => {
+    tempValue.value = v;
+  });
   watch(tempValue, (v) => {
     emit('tempValueChange', tempValue.value);
     if (shouldUpdateValueImmediately)

+ 1 - 1
src/components/form/RadioGroup.vue

@@ -11,7 +11,7 @@ export interface RadioBoxGroupProps {
   /**
    * 当前组选中的项目
    */
-  modelValue?: string|number|boolean;
+  modelValue?: string|number|boolean|undefined|null;
   /**
    * 是否禁用整组复选框,设置后会禁用全部复选框。
    * @default false

+ 2 - 2
src/components/form/UploaderField.vue

@@ -15,7 +15,7 @@ import { stringUrlToUploaderItem, type UploaderItem } from './Uploader';
 import Uploader from './Uploader.vue';
 
 export interface UploaderFieldProps extends Omit<UploaderProps, 'value'> {
-  modelValue?: string[];
+  modelValue?: string[]|string;
   single?: boolean;
 }
 
@@ -46,7 +46,7 @@ onMounted(() => {
   setTimeout(() => {
     uploaderRef.value?.setList(props.single || typeof value.value === 'string' 
       ? (value.value ? [ stringUrlToUploaderItem(value.value as any as string) ] : [])
-      : props.modelValue?.map((item) => stringUrlToUploaderItem(item)) ?? []
+      : value.value?.map((item) => stringUrlToUploaderItem(item)) ?? []
     )
   }, 200);
 });

+ 1 - 0
src/pages/dig/composeable/TaskEntryForm.ts

@@ -9,6 +9,7 @@ export function useTaskEntryForm() {
   
   function goForm(subType: string, subId: number, subKey = 'type', type = 'list', subTitle = '') {
     navTo('/pages/dig/forms/' + type, {
+      id: type === 'common' ? 1 : undefined,
       villageId: querys.value.villageId,  
       villageVolunteerId: querys.value.villageVolunteerId,  
       subType,

+ 28 - 15
src/pages/dig/forms/common.vue

@@ -1,18 +1,20 @@
 <template>
-  <CommonRoot>
-    <LoadingPage v-if="loading" /> 
-    <FlexCol :padding="30">
-      <DynamicForm
-        ref="formRef"
-        :options="formDefine"
-        :model="formModel"formModel
-        :globalParams="querys"
-      />
-      <Height :height="20" />
-      <Button type="primary" @click="submit">提交</Button>
-    </FlexCol>
-    <XBarSpace />
-  </CommonRoot>
+  <view>
+    <CommonRoot>
+      <LoadingPage v-if="loading" /> 
+      <FlexCol :padding="30">
+        <DynamicForm
+          ref="formRef"
+          :options="formDefine"
+          :model="formModel"formModel
+          :globalParams="querys"
+        />
+        <Height :height="20" />
+        <Button type="primary" @click="submit">提交</Button>
+      </FlexCol>
+      <XBarSpace />
+    </CommonRoot>
+  </view>
 </template>
 
 <script setup lang="ts">
@@ -20,7 +22,7 @@ import { nextTick, ref, type Ref } from 'vue';
 import { showError } from '@/common/composeabe/ErrorDisplay';
 import { useLoadQuerys } from '@/common/composeabe/LoadQuerys';
 import { getVillageInfoForm } from './forms';
-import { RequestApiError } from '@imengyu/imengyu-utils';
+import { RequestApiError, waitTimeOut } from '@imengyu/imengyu-utils';
 import VillageInfoApi, { CommonInfoModel } from '@/api/inhert/VillageInfoApi';
 import DynamicForm from '@/components/dynamic/DynamicForm.vue';
 import LoadingPage from '@/components/display/loading/LoadingPage.vue';
@@ -32,6 +34,7 @@ import Height from '@/components/layout/space/Height.vue';
 import { backAndCallOnPageBack } from '@/components/utils/PageAction';
 import XBarSpace from '@/components/layout/space/XBarSpace.vue';
 import { toast } from '@/components/utils/DialogAction';
+import { confirm } from '@/components/dialog/CommonRoot';
 
 const loading = ref(false);
 const subTitle = ref('');
@@ -62,12 +65,20 @@ async function submit() {
   try {
     loading.value = true;
     formModel.value.type = querys.value.subId;
+    await waitTimeOut(800);
     await VillageInfoApi.updateInfo(
       querys.value.subType,
       querys.value.villageId,
       querys.value.villageVolunteerId,
       formModel.value as CommonInfoModel,
     );
+    confirm({
+      content: '您的提交已成功,感谢您的参与!',
+      cancelText: '继续编辑',
+      confirmText: '返回列表',
+      icon: 'success',
+      onConfirm: () => backPrev(true),
+    })
   } catch (e) {
     showError(e);
   } finally {
@@ -98,6 +109,8 @@ const { querys } = useLoadQuerys({
 
   let formData = undefined;
 
+  await waitTimeOut(200);
+
   try {
     const [model, forms] = getVillageInfoForm(querys.subType, querys.subId);
     formModel.value = new model() as any;

+ 20 - 2
src/pages/dig/forms/data/overview.ts

@@ -111,6 +111,7 @@ export const villageInfoOverviewForm : GroupForm = {
           min: -1000,
           max: 10000,
           step: 10,
+          addonAfter: 'M',
         },
         rules:  [{
           required: true,
@@ -138,7 +139,7 @@ export const villageInfoOverviewForm : GroupForm = {
         }],
       },
       { 
-        label: '村域面积(平方公里)', 
+        label: '村域面积', 
         name: 'area', 
         type: 'number', 
         defaultValue: 0,
@@ -147,6 +148,7 @@ export const villageInfoOverviewForm : GroupForm = {
           min: 0,
           max: 10000,
           step: 1,
+          addonAfter: '平方公里',
         },
         rules:  [{
           required: true,
@@ -154,7 +156,7 @@ export const villageInfoOverviewForm : GroupForm = {
         }] 
       }, 
       { 
-        label: '村庄占地面积(亩)', 
+        label: '村庄占地面积', 
         name: 'villageArea', 
         type: 'number', 
         defaultValue: 0,
@@ -163,6 +165,7 @@ export const villageInfoOverviewForm : GroupForm = {
           min: 0,
           max: 10000,
           step: 1,
+          addonAfter: '亩',
         },
         rules:  [{
           required: true,
@@ -500,6 +503,21 @@ export const villageInfoOverviewForm : GroupForm = {
               required: true,
               message: '请输入概括',
             }]
+          },
+          {
+            label: '突出价值',
+            name: 'prominent',
+            type: 'richtext',
+            defaultValue: '',
+            additionalProps: {
+              placeholder: '请输入村落突出价值信息',
+              maxLength: 300,
+              showWordLimit: true, 
+            } as FieldProps,
+            rules: [{
+              required: true,
+              message: '请输入突出价值',
+            }]
           }
         ]
       },

+ 3 - 0
src/pages/dig/forms/list.vue

@@ -25,6 +25,9 @@
       >
         <Image 
           :src="item.image"
+          defaultImage="https://mn.wenlvti.net/app_static/minnan/EmptyImage.png"
+          failedImage="https://mn.wenlvti.net/app_static/minnan/EmptyImage.png"
+          :showFailed="false"
           :width="100"
           :height="100"
           :radius="10"