2026-06-25 a26b31cc9f3ee9b21b1a754e80fa7359e8a7a8f8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<script lang="ts" setup>
import type { StudentApi } from '#/api/infra/demo';
 
import { computed, ref, h, onMounted, watch, nextTick } from 'vue';
 
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
 
import { message, Tabs, Form, Input, Textarea, Button, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker } from 'ant-design-vue';
import { $t } from '#/locales';
 
 import type { VxeTableInstance } from '#/adapter/vxe-table';
import { Plus } from "@vben/icons";
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
import { ImageUpload, FileUpload } from "#/components/upload";
import { getStudentContactListByStudentId } from '#/api/infra/demo';
 
const props = defineProps<{
   studentId?: number // 学生编号(主表的关联字段)
}>()
 
 const list = ref<StudentApi.StudentContact[]>([]) // 列表的数据
const tableRef = ref<VxeTableInstance>();
 
/** 添加学生联系人 */
async function handleAdd() {
  await tableRef.value?.insertAt({} as StudentApi.StudentContact, -1);
}
 
/** 删除学生联系人 */
async function handleDelete(row: StudentApi.StudentContact) {
  await tableRef.value?.remove(row);
}
 
/** 提供获取表格数据的方法供父组件调用 */
defineExpose({
  getData: (): StudentApi.StudentContact[] => {
    const data = list.value as StudentApi.StudentContact[];
    const removeRecords = tableRef.value?.getRemoveRecords() as StudentApi.StudentContact[];
    const insertRecords = tableRef.value?.getInsertRecords() as StudentApi.StudentContact[];
    return [
      ...data.filter(
        (row) => !removeRecords.some((removed) => removed.id === row.id),
      ),
      ...insertRecords.map((row: any) => ({ ...row, id: undefined })),
    ];
  },
});
 
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
    () => props.studentId,
    async (val) => {
      if (!val) {
        return;
      }
      list.value = await getStudentContactListByStudentId(props.studentId!);
    },
    { immediate: true },
);
</script>
 
<template>
   <VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
                 <VxeColumn field="name" title="名字" align="center">
                  <template #default="{ row }">
                    <Input v-model:value="row.name" />
                  </template>
                </VxeColumn>
                <VxeColumn field="description" title="简介" align="center">
                  <template #default="{ row }">
                    <Textarea v-model:value="row.description" />
                  </template>
                </VxeColumn>
                <VxeColumn field="birthday" title="出生日期" align="center">
                  <template #default="{ row }">
                    <DatePicker
                        v-model:value="row.birthday"
                        :showTime="true"
                        format="YYYY-MM-DD HH:mm:ss"
                        valueFormat='x'
                    />
                  </template>
                </VxeColumn>
                <VxeColumn field="sex" title="性别" align="center">
                  <template #default="{ row }">
                    <Select v-model:value="row.sex" placeholder="请选择性别">
                          <Select.Option
                              v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'number')"
                              :key="dict.value"
                              :value="dict.value"
                          >
                            {{ dict.label }}
                          </Select.Option>
                    </Select>
                  </template>
                </VxeColumn>
                <VxeColumn field="enabled" title="是否有效" align="center">
                  <template #default="{ row }">
                    <RadioGroup v-model:value="row.enabled">
                          <Radio
                              v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), 'boolean')"
                              :key="dict.value"
                              :value="dict.value"
                          >
                            {{ dict.label }}
                          </Radio>
                    </RadioGroup>
                  </template>
                </VxeColumn>
                <VxeColumn field="avatar" title="头像" align="center">
                  <template #default="{ row }">
                    <ImageUpload v-model:value="row.avatar" />
                  </template>
                </VxeColumn>
                <VxeColumn field="video" title="附件" align="center">
                  <template #default="{ row }">
                    <FileUpload v-model:value="row.video" />
                  </template>
                </VxeColumn>
                <VxeColumn field="memo" title="备注" align="center">
                  <template #default="{ row }">
                    <Textarea v-model:value="row.memo" />
                  </template>
                </VxeColumn>
    <VxeColumn field="operation" title="操作" align="center">
      <template #default="{ row }">
        <Button
            size="small"
            type="link"
            danger
            @click="handleDelete(row)"
            v-access:code="['infra:student:delete']"
        >
          {{ $t('ui.actionTitle.delete') }}
        </Button>
      </template>
    </VxeColumn>
  </VxeTable>
  <div class="flex justify-center mt-4">
    <Button :icon="h(Plus)" type="primary" ghost @click="handleAdd" v-access:code="['infra:student:create']">
      {{ $t('ui.actionTitle.create', ['学生联系人']) }}
    </Button>
  </div>
</template>