Commit b4372a2a89400c9ecfd525a6bbf88d8473c2f197

Authored by sqy
1 parent e5a3b607

fix:调整oem地区显示,更新地理位置,优化UI界面

... ... @@ -21,6 +21,9 @@
21 21 theme = htmlRoot = null;
22 22 }
23 23 })();
  24 + const historyClick = () => {
  25 + console.log(1);
  26 + };
24 27 </script>
25 28 <div id="app">
26 29 <style>
... ...
... ... @@ -17,6 +17,8 @@ enum DeviceManagerApi {
17 17 DEVICE_PROFILE_URL = '/deviceProfile',
18 18
19 19 DEVICE_PROFILE_URL_ME = '/deviceProfile/me/list',
  20 +
  21 + DEVICE_ALARM_URL = '/alarm/DEVICE',
20 22 }
21 23
22 24 export const devicePage = (params: DeviceQueryParam) => {
... ... @@ -93,3 +95,18 @@ export const getDeviceDetail = (id: string) => {
93 95 url: DeviceManagerApi.DEVICE_URL + `/${id}`,
94 96 });
95 97 };
  98 +
  99 +// 查询设备详情中的告警
  100 +export const getDeviceAlarm = (id: string, params?: DeviceProfileQueryParam) => {
  101 + return () => {
  102 + return defHttp.get(
  103 + {
  104 + url: DeviceManagerApi.DEVICE_ALARM_URL + `/${id}`,
  105 + params,
  106 + },
  107 + {
  108 + joinPrefix: false,
  109 + }
  110 + );
  111 + };
  112 +};
... ...
... ... @@ -9,7 +9,7 @@ export enum DeviceTypeEnum {
9 9 DIRECT_CONNECTION = 'DIRECT_CONNECTION',
10 10 SENSOR = 'SENSOR',
11 11 }
12   -export type DeviceProfileQueryParam = BasicPageParams & DeviceProfileParam;
  12 +export type DeviceProfileQueryParam = BasicPageParams & DeviceProfileParam & DeviceId;
13 13 export type DeviceQueryParam = BasicPageParams & DeviceParam;
14 14 export type DeviceParam = {
15 15 name?: string;
... ... @@ -18,6 +18,9 @@ export type DeviceParam = {
18 18 export type DeviceProfileParam = {
19 19 name?: string;
20 20 };
  21 +export type DeviceId = {
  22 + id?: string;
  23 +};
21 24
22 25 export interface DeviceModel {
23 26 id: string;
... ...
... ... @@ -32,6 +32,7 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
32 32 },
33 33 {
34 34 errorMessageMode: mode,
  35 + joinPrefix: false,
35 36 }
36 37 );
37 38 }
... ...
... ... @@ -32,19 +32,20 @@
32 32 // setTimeout(() => {
33 33 // console.log(isDolang);
34 34 // }, 1500);
  35 +const firstMenu = '/system/account';
35 36
36   -export enum PageEnum {
  37 +export const PageEnum = {
37 38 // basic login path
38   - BASE_LOGIN = '/login',
  39 + BASE_LOGIN: '/login',
39 40 // basic home path
40   - BASE_HOME = '/system/account',
  41 + BASE_HOME: firstMenu,
41 42 // BASE_HOME = '/dashboard',
42 43 // error page path
43   - ERROR_PAGE = '/exception',
  44 + ERROR_PAGE: '/exception',
44 45 // error log page path
45   - ERROR_LOG_PAGE = '/error-log/list',
  46 + ERROR_LOG_PAGE: '/error-log/list',
46 47 //消息配置
47   - MESSAGE_CONFIG = '/config/message',
  48 + MESSAGE_CONFIG: '/config/message',
48 49 //设备配置
49   - DEVICE_PROFILE = '/deviceManager/deviceProfile',
50   -}
  50 + DEVICE_PROFILE: '/deviceManager/deviceProfile',
  51 +};
... ...
... ... @@ -37,7 +37,7 @@ export const copyTransTreeFun = (arr: any[]) => {
37 37 };
38 38
39 39 // 百度地图url
40   -export const register_BAI_DU_MAP_URL = (ak: string) => {
  40 +const register_BAI_DU_MAP_URL = (ak: string) => {
41 41 return `https://api.map.baidu.com/getscript?v=3.0&ak=${ak}`;
42 42 };
43 43
... ...
... ... @@ -42,7 +42,7 @@ export const formSchema: FormSchema[] = [
42 42 componentProps: {
43 43 size: 'small',
44 44 options: [
45   - { label: '全部', value: 'ALL' },
  45 + { label: '全部', value: '' },
46 46 { label: '待激活', value: 'INACTIVE' },
47 47 { label: '在线', value: 'ONLINE' },
48 48 { label: '离线', value: 'OFFLINE' },
... ... @@ -51,14 +51,14 @@ export const formSchema: FormSchema[] = [
51 51 },
52 52 {
53 53 field: 'alarmStatus',
54   - label: '是否警',
  54 + label: '是否警',
55 55 component: 'RadioGroup',
56 56 labelWidth: '85px',
57 57 componentProps: {
58 58 size: 'small',
59 59 options: [
60 60 { label: '是', value: '1' },
61   - { label: '否', value: '2' },
  61 + { label: '否', value: '0' },
62 62 ],
63 63 },
64 64 },
... ...
... ... @@ -64,6 +64,7 @@
64 64 if (!wrapEl) return;
65 65 const map = new BMap.Map(wrapEl);
66 66 const point = new BMap.Point(116.14282, 35.19515);
  67 +
67 68 map.centerAndZoom(point, 15);
68 69 map.enableScrollWheelZoom(true);
69 70 }
... ... @@ -83,6 +84,70 @@
83 84 });
84 85 // 点击表格某一行触发
85 86 const deviceRowClick = (record) => {
  87 + const BMap = (window as any).BMap;
  88 + const wrapEl = unref(wrapRef);
  89 + const map = new BMap.Map(wrapEl);
  90 + if (record.deviceInfo.address) {
  91 + const { name, organizationDTO, updateTime, deviceState } = record;
  92 + const { longitude, latitude, address } = record.deviceInfo;
  93 + const point = new BMap.Point(longitude, latitude);
  94 + let options = {
  95 + width: 300, // 信息窗口宽度
  96 + height: 220, // 信息窗口高度
  97 + };
  98 + map.centerAndZoom(point, 15);
  99 + map.enableScrollWheelZoom(true);
  100 + let infoWindow = new BMap.InfoWindow(
  101 + `
  102 + <div style="display:flex;justify-content:space-between; margin:20px 0px;">
  103 + <div style="font-size:16px;font-weight:bold">${name}</div>
  104 + ${
  105 + deviceState === 'INACTIVE'
  106 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'
  107 + : deviceState === 'ONLINE'
  108 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'
  109 + : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'
  110 + }
  111 + </div>
  112 + <div>所属组织:${organizationDTO.name}</div>
  113 + <div style="margin-top:6px;">接入协议:${address}</div>
  114 + <div style="margin-top:6px;">设备位置:${address}</div>
  115 + <div style="margin-top:6px;">下线时间:${updateTime}</div>
  116 + <div style="display:flex;justify-content:space-between; margin-top:10px">
  117 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
  118 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
  119 + <button onclick="historyClick()"style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
  120 + </div>
  121 + `,
  122 + options
  123 + ); // 创建信息窗口对象
  124 + map.openInfoWindow(infoWindow, map.getCenter());
  125 + let preMarker = null;
  126 + const rivet =
  127 + deviceState === 'INACTIVE'
  128 + ? '/src/assets/images/djx.png'
  129 + : deviceState === 'ONLINE'
  130 + ? '/src/assets/images/zx.png'
  131 + : '/src/assets/images/lx.png';
  132 + let myIcon = new BMap.Icon(rivet, new BMap.Size(20, 30));
  133 + let marker = new BMap.Marker(point, { icon: myIcon });
  134 + if (marker) {
  135 + map.removeOverlay(preMarker);
  136 + }
  137 + map.addOverlay(marker);
  138 + } else {
  139 + const point = new BMap.Point(106.63028229687498, 36.06735821600903);
  140 + let options = {
  141 + width: 100, // 信息窗口宽度
  142 + height: 100, // 信息窗口高度
  143 + title: '提示', // 信息窗口标题
  144 + };
  145 + map.centerAndZoom(point, 5);
  146 + map.enableScrollWheelZoom(true);
  147 + let infoWindow = new BMap.InfoWindow('该设备暂无地理位置', options); // 创建信息窗口对象
  148 + map.openInfoWindow(infoWindow, map.getCenter());
  149 + }
  150 +
86 151 console.log(record);
87 152 };
88 153
... ...
... ... @@ -14,7 +14,7 @@
14 14 <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'"
15 15 ><RealTimeData
16 16 /></TabPane>
17   - <TabPane key="3" tab="告警"><Alarm /></TabPane>
  17 + <TabPane key="3" tab="告警"><Alarm :id="deviceId" /></TabPane>
18 18 <TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'SENSOR'"
19 19 ><ChildDevice
20 20 /></TabPane>
... ... @@ -48,12 +48,15 @@
48 48 const activeKey = ref('1');
49 49 const size = ref('small');
50 50 const deviceDetailRef = ref();
51   - const deviceDetail = ref({});
  51 + const deviceDetail: any = ref({});
  52 + const deviceId = ref('');
52 53 // 详情回显
53 54 const [register] = useDrawerInner(async (data) => {
54 55 const { id } = data;
55 56 const res = await getDeviceDetail(id);
56 57 deviceDetail.value = res;
  58 + deviceId.value = id;
  59 + console.log(deviceId.value);
57 60 const { latitude, longitude, address } = res.deviceInfo;
58 61 if (latitude) {
59 62 deviceDetailRef.value.initMap(longitude, latitude, address);
... ... @@ -70,6 +73,7 @@
70 73 closeDrawer,
71 74 deviceDetail,
72 75 deviceDetailRef,
  76 + deviceId,
73 77 };
74 78 },
75 79 });
... ...
... ... @@ -5,13 +5,24 @@
5 5 import { defineComponent } from 'vue';
6 6 import { BasicTable, useTable } from '/@/components/Table';
7 7 import { alarmColumns, alarmSearchSchemas } from '../../config/detail.config';
  8 + import { getDeviceAlarm } from '/@/api/device/deviceManager';
8 9 export default defineComponent({
9 10 name: 'DeviceManagement',
10 11 components: {
11 12 BasicTable,
12 13 },
13   - setup(_) {
  14 + props: {
  15 + id: {
  16 + type: String,
  17 + required: true,
  18 + },
  19 + },
  20 + setup(props) {
14 21 const [registerTable] = useTable({
  22 + api: getDeviceAlarm(props.id, {
  23 + page: 0,
  24 + pageSize: 10,
  25 + }),
15 26 columns: alarmColumns,
16 27 formConfig: {
17 28 labelWidth: 120,
... ...
... ... @@ -16,6 +16,7 @@
16 16 import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
17 17 import { saveTenantAdmin } from '/@/api/tenant/tenantApi';
18 18 import { UserDTO } from '/@/api/tenant/tenantInfo';
  19 + import { emailRule, phoneRule } from '/@/utils/rules';
19 20
20 21 export default defineComponent({
21 22 name: 'TenantAdminFormDrawer',
... ... @@ -51,12 +52,14 @@
51 52 label: '电话号码:',
52 53 required: true,
53 54 component: 'Input',
  55 + rules: phoneRule,
54 56 },
55 57 {
56 58 field: 'email',
57 59 label: '邮件:',
58 60 required: true,
59 61 component: 'Input',
  62 + rules: emailRule,
60 63 },
61 64 {
62 65 field: 'enabled',
... ...
... ... @@ -63,7 +63,7 @@ export const schemas: FormSchema[] = [
63 63 },
64 64 },
65 65 {
66   - field: 'country',
  66 + field: 'nameCountry',
67 67 component: 'ApiSelect',
68 68 label: '国家/地区',
69 69 colProps: {
... ... @@ -73,7 +73,7 @@ export const schemas: FormSchema[] = [
73 73 api: getAreaList,
74 74 params: { parentId: 0 },
75 75 labelField: 'name',
76   - valueField: 'parentId',
  76 + valueField: 'code',
77 77 placeholder: '请选择国家/地区',
78 78 },
79 79 },
... ... @@ -280,4 +280,10 @@ export const schemas: FormSchema[] = [
280 280 },
281 281 slot: 'qrcode',
282 282 },
  283 + {
  284 + field: 'id',
  285 + label: '唯一id',
  286 + component: 'Input',
  287 + show: false,
  288 + },
283 289 ];
... ...
... ... @@ -43,14 +43,15 @@
43 43 import { Card, Upload } from 'ant-design-vue';
44 44 import { BasicForm, useForm } from '/@/components/Form/index';
45 45 import { schemas } from '../config/enterPriseInfo.config';
46   - import { getEnterPriseDetail, updateEnterPriseDetail } from '/@/api/oem/index';
  46 + import { getAreaList, getEnterPriseDetail, updateEnterPriseDetail } from '/@/api/oem/index';
47 47 import { Loading } from '/@/components/Loading';
48 48 import { useMessage } from '/@/hooks/web/useMessage';
49 49 import { useUserStore } from '/@/store/modules/user';
50 50 import { createLocalStorage } from '/@/utils/cache';
51 51 import { PlusOutlined } from '@ant-design/icons-vue';
52   - import type { FileItem } from '/@/components/Upload/src/typing';
53 52 import { qrcodeUpload } from '/@/api/oem/index';
  53 + import type { FileItem } from '/@/components/Upload/src/typing';
  54 + import type { CityItem, Code } from '../types';
54 55 export default defineComponent({
55 56 components: {
56 57 Card,
... ... @@ -102,22 +103,25 @@
102 103 const handleUpdateInfo = async () => {
103 104 try {
104 105 const fieldsValue = getFieldsValue();
105   - const newFieldValue = {
  106 + const newFieldValue: any = {
106 107 ...fieldsValue,
107   - codeProv: fieldsValue.nameProv,
108   - codeCity: fieldsValue.nameCity,
109   - codeCoun: fieldsValue.nameCoun,
110 108 codeTown: fieldsValue.nameTown,
111 109 qrCode: qrcodePic.value,
112 110 };
  111 + delete newFieldValue.nameProv;
  112 + delete newFieldValue.nameCity;
  113 + delete newFieldValue.nameCoun;
  114 + delete newFieldValue.nameTown;
  115 +
113 116 // 表单校验
114 117 await validate();
115 118 compState.value.loading = true;
116 119 await updateEnterPriseDetail(newFieldValue);
117   - compState.value.loading = false;
118 120 createMessage.success('更新信息成功');
119 121 setEnterPriseInfo(newFieldValue);
120   - } catch (e) {}
  122 + } finally {
  123 + compState.value.loading = false;
  124 + }
121 125 };
122 126
123 127 const userStore = useUserStore();
... ... @@ -131,137 +135,166 @@
131 135 storage.set('enterpriseInfo', newFieldValue);
132 136 }
133 137
134   - // // 地区显示回显和数据联动
135   - // async function updateCityData(
136   - // codeProv: string,
137   - // codeCity: string,
138   - // codeCoun: string,
139   - // codeTown: string
140   - // ) {
141   - // const nameCity = await getTownChild('codeProv', codeProv);
142   - // const nameCoun = await getTownChild('codeCity', codeCity);
143   - // const nameTown = await getTownChild('codeCoun', codeCoun);
144   - // nameCity.forEach((item) => {
145   - // item.label = item.nameCity;
146   - // item.value = item.codeCity;
147   - // });
148   - // nameCoun.forEach((item) => {
149   - // item.label = item.nameCoun;
150   - // item.value = item.codeCoun;
151   - // });
152   - // nameTown.forEach((item) => {
153   - // item.label = item.nameTown;
154   - // item.value = item.codeTown;
155   - // });
156   - // setFieldsValue({
157   - // nameProv: codeProv,
158   - // nameCity: codeCity,
159   - // nameCoun: codeCoun,
160   - // nameTown: codeTown,
161   - // });
162   - // updateSchema({
163   - // field: 'nameTown',
164   - // componentProps: {
165   - // options: nameTown,
166   - // },
167   - // });
168   - // updateSchema({
169   - // field: 'nameCoun',
170   - // componentProps: {
171   - // options: nameCoun,
172   - // async onChange(value) {
173   - // if (value === undefined) {
174   - // setFieldsValue({
175   - // nameTown: undefined,
176   - // });
177   - // updateSchema({
178   - // field: 'nameTown',
179   - // componentProps: {
180   - // options: [],
181   - // },
182   - // });
183   - // }
184   - // let nameTown = await getTownChild('codeCoun', value);
185   - // nameTown.forEach((item) => {
186   - // item.label = item.nameTown;
187   - // item.value = item.codeTown;
188   - // });
189   - // setFieldsValue({
190   - // nameTown: undefined,
191   - // });
192   - // updateSchema({
193   - // field: 'nameTown',
194   - // componentProps: {
195   - // placeholder: '请选择街道/城镇',
196   - // options: nameTown,
197   - // },
198   - // });
199   - // },
200   - // },
201   - // });
202   - // updateSchema({
203   - // field: 'nameCity',
204   - // componentProps: ({ formModel }) => {
205   - // return {
206   - // options: nameCity,
207   - // onChange: async (value) => {
208   - // let nameCoun = await getTownChild('codeCity', value);
209   - // if (value === undefined) {
210   - // formModel.nameCoun = undefined; // reset city value
211   - // formModel.nameTown = undefined;
212   - // nameCoun = [];
213   - // updateSchema({
214   - // field: 'nameTown',
215   - // componentProps: {
216   - // options: [],
217   - // },
218   - // });
219   - // }
220   - // nameCoun.forEach((item) => {
221   - // item.label = item.nameCoun;
222   - // item.value = item.codeCoun;
223   - // });
224   - // formModel.nameCoun = undefined; // reset city value
225   - // formModel.nameTown = undefined;
226   - // updateSchema({
227   - // field: 'nameCoun',
228   - // componentProps: {
229   - // // 请选择区
230   - // options: nameCoun,
231   - // async onChange(value) {
232   - // let nameTown = await getTownChild('codeCoun', value);
233   - // if (value === undefined) {
234   - // formModel.nameTown = undefined;
235   - // nameTown = [];
236   - // }
237   - // nameTown.forEach((item) => {
238   - // item.label = item.nameTown;
239   - // item.value = item.codeTown;
240   - // });
  138 + // 地区显示回显和数据联动
  139 + async function updateCityData(
  140 + provs: CityItem[],
  141 + cities: CityItem[],
  142 + couns: CityItem[],
  143 + towns: CityItem[],
  144 + code: Code
  145 + ) {
  146 + // 加工后端返回字段
  147 + provs.forEach((item) => {
  148 + item.label = item.name;
  149 + item.value = item.code;
  150 + });
  151 + cities.forEach((item) => {
  152 + item.label = item.name;
  153 + item.value = item.code;
  154 + });
  155 + couns.forEach((item) => {
  156 + item.label = item.name;
  157 + item.value = item.code;
  158 + });
  159 + towns.forEach((item) => {
  160 + item.label = item.name;
  161 + item.value = item.code;
  162 + });
  163 + const { codeCountry, codeProv, codeCity, codeCoun, codeTown } = code;
  164 + setFieldsValue({
  165 + nameCountry: codeCountry,
  166 + nameProv: codeProv,
  167 + nameCity: codeCity,
  168 + nameCoun: codeCoun,
  169 + nameTown: codeTown,
  170 + });
  171 + updateSchema([
  172 + {
  173 + field: 'nameCity',
  174 + componentProps: ({ formModel }) => {
  175 + return {
  176 + options: cities,
  177 + async onChange(value) {
  178 + let couns: CityItem[] = await getAreaList({ parentId: value });
  179 + if (value === undefined) {
  180 + formModel.nameCoun = undefined; // reset city value
  181 + formModel.nameTown = undefined;
  182 + updateSchema({
  183 + field: 'nameTown',
  184 + componentProps: {
  185 + options: [],
  186 + },
  187 + });
  188 + } else {
  189 + couns.forEach((item) => {
  190 + item.label = item.name;
  191 + item.value = item.code;
  192 + });
  193 + formModel.nameCoun = undefined; // reset city value
  194 + formModel.nameTown = undefined;
  195 + updateSchema({
  196 + field: 'nameCoun',
  197 + componentProps: {
  198 + // 请选择区
  199 + options: couns,
  200 + async onChange(value) {
  201 + let towns: CityItem[] = await getAreaList({ parentId: value });
  202 + if (value === undefined) {
  203 + formModel.nameTown = undefined;
  204 + }
  205 + towns.forEach((item) => {
  206 + item.label = item.name;
  207 + item.value = item.code;
  208 + });
241 209
242   - // formModel.nameTown = undefined;
243   - // updateSchema({
244   - // field: 'nameTown',
245   - // componentProps: {
246   - // placeholder: '请选择街道/城镇',
247   - // options: nameTown,
248   - // },
249   - // });
250   - // },
251   - // },
252   - // });
253   - // },
254   - // };
255   - // },
256   - // });
257   - // }
  210 + formModel.nameTown = undefined;
  211 + updateSchema({
  212 + field: 'nameTown',
  213 + componentProps: {
  214 + placeholder: '请选择街道/城镇',
  215 + options: towns,
  216 + },
  217 + });
  218 + },
  219 + },
  220 + });
  221 + }
  222 + },
  223 + };
  224 + },
  225 + },
  226 + {
  227 + field: 'nameCoun',
  228 + componentProps: {
  229 + options: couns,
  230 + async onChange(value) {
  231 + if (value === undefined) {
  232 + setFieldsValue({
  233 + nameTown: undefined,
  234 + });
  235 + updateSchema({
  236 + field: 'nameTown',
  237 + componentProps: {
  238 + options: [],
  239 + },
  240 + });
  241 + } else {
  242 + let towns = await getAreaList({ parentId: value });
  243 + towns.forEach((item) => {
  244 + item.label = item.name;
  245 + item.value = item.code;
  246 + });
  247 + setFieldsValue({
  248 + nameTown: undefined,
  249 + });
  250 + updateSchema({
  251 + field: 'nameTown',
  252 + componentProps: {
  253 + placeholder: '请选择街道/城镇',
  254 + options: towns,
  255 + },
  256 + });
  257 + }
  258 + },
  259 + },
  260 + },
  261 + {
  262 + field: 'nameTown',
  263 + componentProps: {
  264 + options: towns,
  265 + },
  266 + },
  267 + ]);
  268 + }
258 269
259 270 onMounted(async () => {
260 271 const res = await getEnterPriseDetail();
261   - // const { codeProv, codeCity, codeCoun, codeTown } = res;
262   - // updateCityData(codeProv, codeCity, codeCoun, codeTown);
263   - setFieldsValue(res);
264   - qrcodePic.value = res.qrCode;
  272 + if (res.sysTown) {
  273 + const {
  274 + provs,
  275 + cities,
  276 + couns,
  277 + towns,
  278 + codeCountry,
  279 + codeProv,
  280 + codeCity,
  281 + codeCoun,
  282 + codeTown,
  283 + } = res.sysTown;
  284 + const code = {
  285 + codeCountry,
  286 + codeProv,
  287 + codeCity,
  288 + codeCoun,
  289 + codeTown,
  290 + };
  291 + updateCityData(provs, cities, couns, towns, code);
  292 + setFieldsValue(res);
  293 + qrcodePic.value = res.qrCode;
  294 + } else {
  295 + setFieldsValue(res);
  296 + qrcodePic.value = res.qrCode;
  297 + }
265 298 });
266 299
267 300 return {
... ...
... ... @@ -13,3 +13,19 @@ export interface FileInfo {
13 13 file: FileItem;
14 14 fileList: FileItem[];
15 15 }
  16 +
  17 +export interface CityItem {
  18 + code: number;
  19 + level: string;
  20 + name: string;
  21 + parentId: number;
  22 + label?: string;
  23 + value?: number;
  24 +}
  25 +export interface Code {
  26 + codeCountry: number;
  27 + codeProv: number;
  28 + codeCity: number;
  29 + codeCoun: number;
  30 + codeTown: number;
  31 +}
... ...