提交 b5a25e0b authored 作者: 王鹏飞's avatar 王鹏飞

chore: update

上级 28abbff8
...@@ -36,1106 +36,19 @@ export function getMapList() { ...@@ -36,1106 +36,19 @@ export function getMapList() {
// 我的数据集列表 // 我的数据集列表
export function getMyList(params?: Partial<{ page: number; 'per-page': number }>) { export function getMyList(params?: Partial<{ page: number; 'per-page': number }>) {
// return Promise.resolve({
// code: 0,
// message: 'OK',
// data: {
// total: 10,
// title: [
// {
// name: '序号',
// english_name: 'pk_id',
// type: 'INT',
// },
// {
// name: '订单编号',
// english_name: 'order_number',
// type: 'VARCHAR(7)',
// },
// {
// name: '品牌',
// english_name: 'brand',
// type: 'VARCHAR(12)',
// },
// {
// name: '店铺',
// english_name: 'store',
// type: 'VARCHAR(21)',
// },
// {
// name: '负责人',
// english_name: 'person_in_charge',
// type: 'VARCHAR(9)',
// },
// {
// name: '商品',
// english_name: 'product',
// type: 'VARCHAR(21)',
// },
// {
// name: '颜色',
// english_name: 'color',
// type: 'VARCHAR(6)',
// },
// {
// name: '客户购买类型',
// english_name: 'customer_purchase_type',
// type: 'VARCHAR(6)',
// },
// {
// name: '销量',
// english_name: 'sales_volume',
// type: 'SMALLINT',
// },
// {
// name: '单价',
// english_name: 'unit_price',
// type: 'DECIMAL(5, 2)',
// },
// {
// name: '销售额',
// english_name: 'sales_amount',
// type: 'DECIMAL(6, 2)',
// },
// {
// name: '是否优惠',
// english_name: 'is_discounted',
// type: 'VARCHAR(18)',
// },
// {
// name: '优惠金额',
// english_name: 'discount_amount',
// type: 'SMALLINT',
// },
// {
// name: '实际单价',
// english_name: 'actual_unit_price',
// type: 'DECIMAL(5, 2)',
// },
// {
// name: '实际付款',
// english_name: 'actual_payment',
// type: 'DECIMAL(6, 2)',
// },
// {
// name: '成本',
// english_name: 'cost',
// type: 'DECIMAL(7, 3)',
// },
// {
// name: '利润',
// english_name: 'profit',
// type: 'DECIMAL(6, 3)',
// },
// {
// name: '客户性别',
// english_name: 'customer_gender',
// type: 'TINYINT',
// },
// {
// name: '客户年龄',
// english_name: 'customer_age',
// type: 'VARCHAR(5)',
// },
// {
// name: '会员情况',
// english_name: 'membership_status',
// type: 'VARCHAR(12)',
// },
// {
// name: '是否访问页面',
// english_name: 'has_visited_page',
// type: 'VARCHAR(3)',
// },
// {
// name: '访问页面时长',
// english_name: 'page_visit_duration',
// type: 'TINYINT',
// },
// {
// name: '交易状态',
// english_name: 'transaction_status',
// type: 'VARCHAR(12)',
// },
// {
// name: '商品状态',
// english_name: 'product_status',
// type: 'VARCHAR(9)',
// },
// {
// name: '收货人姓名',
// english_name: 'recipient_name',
// type: 'VARCHAR(9)',
// },
// {
// name: '收货人电话',
// english_name: 'recipient_phone',
// type: 'VARCHAR(11)',
// },
// {
// name: '发货地址',
// english_name: 'shipping_address',
// type: 'VARCHAR(9)',
// },
// {
// name: '收货地址省份',
// english_name: 'recipient_province',
// type: 'VARCHAR(21)',
// },
// {
// name: '收货地址',
// english_name: 'recipient_address',
// type: 'VARCHAR(39)',
// },
// {
// name: '物流公司',
// english_name: 'logistics_company',
// type: 'VARCHAR(15)',
// },
// {
// name: '运单号',
// english_name: 'waybill_number',
// type: 'VARCHAR(7)',
// },
// {
// name: '运送方式',
// english_name: 'shipping_method',
// type: 'VARCHAR(12)',
// },
// {
// name: '支付时间',
// english_name: 'payment_time',
// type: 'VARCHAR(10)',
// },
// {
// name: '预计到达时间',
// english_name: 'estimated_arrival_time',
// type: 'VARCHAR(10)',
// },
// {
// name: '实际到达时间',
// english_name: 'actual_arrival_time',
// type: 'VARCHAR(10)',
// },
// {
// name: '快递反馈',
// english_name: 'courier_feedback',
// type: 'VARCHAR(6)',
// },
// {
// name: '是否退货',
// english_name: 'is_returned',
// type: 'VARCHAR(3)',
// },
// {
// name: '退款原因',
// english_name: 'refund_reason',
// type: 'VARCHAR(9)',
// },
// {
// name: '客户满意度',
// english_name: 'customer_satisfaction',
// type: 'VARCHAR(9)',
// },
// {
// name: '出生日期',
// english_name: 'date_of_birth',
// type: 'VARCHAR(17)',
// },
// {
// name: '品类',
// english_name: 'category',
// type: 'VARCHAR(9)',
// },
// {
// name: '材质',
// english_name: 'material',
// type: 'VARCHAR(6)',
// },
// ],
// list: [
// {
// pk_id: '1',
// order_number: 'A929818',
// brand: '丽丽',
// store: '卡卡家女装',
// person_in_charge: '袁英',
// product: '安全裤',
// color: '红色',
// customer_purchase_type: '零售',
// sales_volume: '256',
// unit_price: '156.14',
// sales_amount: '156.14',
// is_discounted: '店铺活动优惠',
// discount_amount: '12',
// actual_unit_price: '144.14',
// actual_payment: '144.14',
// cost: '99.460',
// profit: '44.680',
// customer_gender: '0',
// customer_age: '33岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '2',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '孙倩',
// recipient_phone: '181****4056',
// shipping_address: '福建省',
// recipient_province: '陕西省',
// recipient_address: '陕西省延安市子长县',
// logistics_company: ' 百世快递',
// waybill_number: 'L810590',
// shipping_method: '公路运输',
// payment_time: '6/21/2020',
// estimated_arrival_time: '6/26/2020',
// actual_arrival_time: '6/26/2020',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '中评',
// date_of_birth: '1989年10月26日',
// category: '女裤',
// material: '麻质',
// },
// {
// pk_id: '2',
// order_number: 'A451538',
// brand: '雅羊人',
// store: '秋兰女装专卖店',
// person_in_charge: '刘佳',
// product: '不规则连衣裙',
// color: '粉色',
// customer_purchase_type: '零售',
// sales_volume: '198',
// unit_price: '247.44',
// sales_amount: '247.44',
// is_discounted: '无',
// discount_amount: '0',
// actual_unit_price: '247.44',
// actual_payment: '247.44',
// cost: '158.360',
// profit: '89.080',
// customer_gender: '0',
// customer_age: '36岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '8',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '余玉英',
// recipient_phone: '181****8027',
// shipping_address: '广东省',
// recipient_province: '⼴西壮族⾃治区',
// recipient_address: '广西壮族自治区贵港市',
// logistics_company: '极兔快 递',
// waybill_number: 'L806051',
// shipping_method: '公路运输',
// payment_time: '4/13/2023',
// estimated_arrival_time: '4/15/2023',
// actual_arrival_time: '4/15/2023',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '好评',
// date_of_birth: '1986年7月17日',
// category: '连衣裙',
// material: '氨纶',
// },
// {
// pk_id: '3',
// order_number: 'A479212',
// brand: '威兰西',
// store: '威兰西旗舰店',
// person_in_charge: '陈建平',
// product: '网红同款连衣裙',
// color: '白色',
// customer_purchase_type: '零售',
// sales_volume: '128',
// unit_price: '245.49',
// sales_amount: '245.49',
// is_discounted: '双12优惠',
// discount_amount: '15',
// actual_unit_price: '230.49',
// actual_payment: '230.49',
// cost: '117.550',
// profit: '112.940',
// customer_gender: '0',
// customer_age: '22岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '4',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '杨军',
// recipient_phone: '139****7024',
// shipping_address: '浙江省',
// recipient_province: '河北省',
// recipient_address: '河北省衡水市武强县',
// logistics_company: '顺丰快递',
// waybill_number: 'L356804',
// shipping_method: '公路运输',
// payment_time: '12/1/2019',
// estimated_arrival_time: '12/6/2019',
// actual_arrival_time: '12/7/2019',
// courier_feedback: '延后',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '好评',
// date_of_birth: '2000年5月3日',
// category: '连衣裙',
// material: '棉质',
// },
// {
// pk_id: '4',
// order_number: 'A389270',
// brand: 'H&R',
// store: '壹佰女装',
// person_in_charge: '杨兰英',
// product: '蕾丝花边T恤',
// color: '紫色',
// customer_purchase_type: '批发',
// sales_volume: '13',
// unit_price: '168.30',
// sales_amount: '2187.90',
// is_discounted: '双11优惠',
// discount_amount: '200',
// actual_unit_price: '152.92',
// actual_payment: '1987.90',
// cost: '1013.830',
// profit: '974.070',
// customer_gender: '0',
// customer_age: '26岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '4',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '郑强',
// recipient_phone: '159****6299',
// shipping_address: '四川省',
// recipient_province: '河南省',
// recipient_address: '河南省郑州市新密市',
// logistics_company: '顺丰快递',
// waybill_number: 'L335437',
// shipping_method: '公路运输',
// payment_time: '11/10/2019',
// estimated_arrival_time: '11/14/2019',
// actual_arrival_time: '11/15/2019',
// courier_feedback: '延后',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '好评',
// date_of_birth: '1996年5月4日',
// category: '上衣',
// material: '棉质',
// },
// {
// pk_id: '5',
// order_number: 'A327122',
// brand: 'H&R',
// store: 'H&R旗舰店',
// person_in_charge: '袁英',
// product: '春夏新款连衣裙',
// color: '红色',
// customer_purchase_type: '批发',
// sales_volume: '13',
// unit_price: '119.64',
// sales_amount: '1555.32',
// is_discounted: '无',
// discount_amount: '0',
// actual_unit_price: '119.64',
// actual_payment: '1555.32',
// cost: '839.870',
// profit: '715.450',
// customer_gender: '0',
// customer_age: '22岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '8',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '刘冬梅',
// recipient_phone: '185****4586',
// shipping_address: '广东省',
// recipient_province: '陕西省',
// recipient_address: '陕西省延安市富县',
// logistics_company: '中通快递',
// waybill_number: 'L209106',
// shipping_method: '公路运输',
// payment_time: '4/28/2019',
// estimated_arrival_time: '4/30/2019',
// actual_arrival_time: '4/30/2019',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '无评论',
// date_of_birth: '2000年7月3日',
// category: '连衣裙',
// material: '氨纶',
// },
// {
// pk_id: '6',
// order_number: 'A577484',
// brand: 'H&R',
// store: 'COCO女装',
// person_in_charge: '刘佳',
// product: '秋冬款连衣裙',
// color: '粉色',
// customer_purchase_type: '批发',
// sales_volume: '12',
// unit_price: '173.70',
// sales_amount: '2084.40',
// is_discounted: '店铺活动优惠',
// discount_amount: '100',
// actual_unit_price: '165.37',
// actual_payment: '1984.40',
// cost: '1091.420',
// profit: '892.980',
// customer_gender: '0',
// customer_age: '61岁',
// membership_status: '钻石会员',
// has_visited_page: '是',
// page_visit_duration: '7',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '谢博',
// recipient_phone: '147****5024',
// shipping_address: '广东省',
// recipient_province: '⽢肃省',
// recipient_address: '甘肃省甘南藏族自治州卓尼县',
// logistics_company: '申通快递',
// waybill_number: 'L913348',
// shipping_method: '航空运输',
// payment_time: '4/7/2019',
// estimated_arrival_time: '4/11/2019',
// actual_arrival_time: '4/11/2019',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '中评',
// date_of_birth: '1961年8月12日',
// category: '连衣裙',
// material: '麻质',
// },
// {
// pk_id: '7',
// order_number: 'A293474',
// brand: '拉夏贝尔',
// store: '拉夏贝尔旗舰店',
// person_in_charge: '刘佳',
// product: '泫雅风T恤',
// color: '粉色',
// customer_purchase_type: '批发',
// sales_volume: '12',
// unit_price: '162.02',
// sales_amount: '1944.24',
// is_discounted: '双11优惠',
// discount_amount: '200',
// actual_unit_price: '145.35',
// actual_payment: '1744.24',
// cost: '1186.080',
// profit: '558.160',
// customer_gender: '0',
// customer_age: '59岁',
// membership_status: '普通会员',
// has_visited_page: '是',
// page_visit_duration: '2',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '赵丽丽',
// recipient_phone: '186****8566',
// shipping_address: '浙江省',
// recipient_province: '⼴东省',
// recipient_address: '广东省梅州市',
// logistics_company: '圆通快递',
// waybill_number: 'L841704',
// shipping_method: '铁路运输',
// payment_time: '11/10/2019',
// estimated_arrival_time: '11/15/2019',
// actual_arrival_time: '11/15/2019',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '中评',
// date_of_birth: '1963年5月27日',
// category: '上衣',
// material: '氨纶',
// },
// {
// pk_id: '8',
// order_number: 'A424004',
// brand: '艾米丽',
// store: '卡卡家女装',
// person_in_charge: '宋建华',
// product: '大码牛仔裤',
// color: '红色',
// customer_purchase_type: '批发',
// sales_volume: '11',
// unit_price: '73.54',
// sales_amount: '808.94',
// is_discounted: '双11优惠',
// discount_amount: '100',
// actual_unit_price: '64.45',
// actual_payment: '708.94',
// cost: '425.360',
// profit: '283.580',
// customer_gender: '0',
// customer_age: '23岁',
// membership_status: '无会员',
// has_visited_page: '是',
// page_visit_duration: '8',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '谢雷',
// recipient_phone: '150****7487',
// shipping_address: '浙江省',
// recipient_province: '宁夏回族自治区',
// recipient_address: '宁夏回族自治区银川市贺兰县',
// logistics_company: ' 百世快递',
// waybill_number: 'L451724',
// shipping_method: '公路运输',
// payment_time: '11/27/2023',
// estimated_arrival_time: '12/1/2023',
// actual_arrival_time: '12/1/2023',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '好评',
// date_of_birth: '1999年12月18日',
// category: '女裤',
// material: '锦纶',
// },
// {
// pk_id: '9',
// order_number: 'A561021',
// brand: 'TARA',
// store: 'TARA旗舰店',
// person_in_charge: '邹勇',
// product: '春夏季新款裤子',
// color: '橙色',
// customer_purchase_type: '批发',
// sales_volume: '11',
// unit_price: '57.25',
// sales_amount: '629.75',
// is_discounted: '双12优惠',
// discount_amount: '150',
// actual_unit_price: '43.61',
// actual_payment: '479.75',
// cost: '287.850',
// profit: '191.900',
// customer_gender: '0',
// customer_age: '36岁',
// membership_status: '普通会员',
// has_visited_page: '是',
// page_visit_duration: '7',
// transaction_status: '交易成功',
// product_status: '已收货',
// recipient_name: '王桂珍',
// recipient_phone: '137****8424',
// shipping_address: '福建省',
// recipient_province: '湖南省',
// recipient_address: '湖南省永州市市辖区',
// logistics_company: '申通快递',
// waybill_number: 'L948843',
// shipping_method: '公路运输',
// payment_time: '12/17/2019',
// estimated_arrival_time: '12/19/2019',
// actual_arrival_time: '12/19/2019',
// courier_feedback: '准时',
// is_returned: '否',
// refund_reason: '无',
// customer_satisfaction: '好评',
// date_of_birth: '1986年6月1日',
// category: '女裤',
// material: '锦纶',
// },
// {
// pk_id: '10',
// order_number: 'A191677',
// brand: '富贵人',
// store: '富贵人旗舰店',
// person_in_charge: '刘佳',
// product: '蕾丝花边T恤',
// color: '粉色',
// customer_purchase_type: '批发',
// sales_volume: '11',
// unit_price: '117.92',
// sales_amount: '1297.12',
// is_discounted: '无',
// discount_amount: '0',
// actual_unit_price: '117.92',
// actual_payment: '1297.12',
// cost: '674.500',
// profit: '622.620',
// customer_gender: '0',
// customer_age: '38岁',
// membership_status: '无会员',
// has_visited_page: '否',
// page_visit_duration: '0',
// transaction_status: '交易取消',
// product_status: '已取消',
// recipient_name: '梁秀华',
// recipient_phone: '136****1451',
// shipping_address: '广东省',
// recipient_province: '⼭东省',
// recipient_address: '山东省聊城市冠县',
// logistics_company: '申通快递',
// waybill_number: 'L385723',
// shipping_method: '航空运输',
// payment_time: '12/3/2019',
// estimated_arrival_time: '12/8/2019',
// actual_arrival_time: '12/8/2019',
// courier_feedback: '准时',
// is_returned: '是',
// refund_reason: '不喜欢',
// customer_satisfaction: '无评论',
// date_of_birth: '1984年2月27日',
// category: '上衣',
// material: '棉质',
// },
// ],
// info: {
// id: '7306145818979860480',
// experiment_id: '7028276368903241728',
// name: '王',
// table_name: '7028276368903241728_6602032005293015040_my_data',
// type: '2',
// source: {
// name: '《商务数据分析基础》数据集-电子商务-10条.xlsx',
// url: 'https://webapp-pub.ezijing.com/upload/saas-bi/f6a1ab335e0a19935b750537e5d8a6f2.xlsx',
// },
// created_operator: '6602032005293015040',
// created_time: '2025-03-14 10:55:04',
// updated_operator: '6602032005293015040',
// updated_time: '2025-03-14 16:31:47',
// },
// },
// })
return httpRequest.get('/api/resource/bi/v1/data/my/list', { params }) return httpRequest.get('/api/resource/bi/v1/data/my/list', { params })
} }
// 查看字段详情 // 查看字段详情
export function getMyField() { export function getMyField() {
// return Promise.resolve({
// code: 0,
// message: 'OK',
// data: [
// {
// name: '订单编号',
// english_name: 'order_number',
// type: 'VARCHAR(7)',
// length: '7',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '品牌',
// english_name: 'brand',
// type: 'VARCHAR(12)',
// length: '12',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '店铺',
// english_name: 'store',
// type: 'VARCHAR(21)',
// length: '21',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '负责人',
// english_name: 'person_in_charge',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '商品',
// english_name: 'product',
// type: 'VARCHAR(21)',
// length: '21',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '颜色',
// english_name: 'color',
// type: 'VARCHAR(6)',
// length: '6',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '客户购买类型',
// english_name: 'customer_purchase_type',
// type: 'VARCHAR(6)',
// length: '6',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '销量',
// english_name: 'sales_volume',
// type: 'SMALLINT',
// length: '',
// point: '',
// null: '允许',
// default: '',
// type_name: '整数',
// },
// {
// name: '单价',
// english_name: 'unit_price',
// type: 'DECIMAL(5, 2)',
// length: '5',
// point: '2',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '销售额',
// english_name: 'sales_amount',
// type: 'DECIMAL(6, 2)',
// length: '6',
// point: '2',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '是否优惠',
// english_name: 'is_discounted',
// type: 'VARCHAR(18)',
// length: '18',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '优惠金额',
// english_name: 'discount_amount',
// type: 'SMALLINT',
// length: '',
// point: '',
// null: '允许',
// default: '',
// type_name: '整数',
// },
// {
// name: '实际单价',
// english_name: 'actual_unit_price',
// type: 'DECIMAL(5, 2)',
// length: '5',
// point: '2',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '实际付款',
// english_name: 'actual_payment',
// type: 'DECIMAL(6, 2)',
// length: '6',
// point: '2',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '成本',
// english_name: 'cost',
// type: 'DECIMAL(7, 3)',
// length: '7',
// point: '3',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '利润',
// english_name: 'profit',
// type: 'DECIMAL(6, 3)',
// length: '6',
// point: '3',
// null: '允许',
// default: '',
// type_name: '小数',
// },
// {
// name: '客户性别',
// english_name: 'customer_gender',
// type: 'TINYINT',
// length: '',
// point: '',
// null: '允许',
// default: '',
// type_name: '整数',
// },
// {
// name: '客户年龄',
// english_name: 'customer_age',
// type: 'VARCHAR(5)',
// length: '5',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '会员情况',
// english_name: 'membership_status',
// type: 'VARCHAR(12)',
// length: '12',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '是否访问页面',
// english_name: 'has_visited_page',
// type: 'VARCHAR(3)',
// length: '3',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '访问页面时长',
// english_name: 'page_visit_duration',
// type: 'TINYINT',
// length: '',
// point: '',
// null: '允许',
// default: '',
// type_name: '整数',
// },
// {
// name: '交易状态',
// english_name: 'transaction_status',
// type: 'VARCHAR(12)',
// length: '12',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '商品状态',
// english_name: 'product_status',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '收货人姓名',
// english_name: 'recipient_name',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '收货人电话',
// english_name: 'recipient_phone',
// type: 'VARCHAR(11)',
// length: '11',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '发货地址',
// english_name: 'shipping_address',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '收货地址省份',
// english_name: 'recipient_province',
// type: 'VARCHAR(21)',
// length: '21',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '收货地址',
// english_name: 'recipient_address',
// type: 'VARCHAR(39)',
// length: '39',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '物流公司',
// english_name: 'logistics_company',
// type: 'VARCHAR(15)',
// length: '15',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '运单号',
// english_name: 'waybill_number',
// type: 'VARCHAR(7)',
// length: '7',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '运送方式',
// english_name: 'shipping_method',
// type: 'VARCHAR(12)',
// length: '12',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '支付时间',
// english_name: 'payment_time',
// type: 'VARCHAR(10)',
// length: '10',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '预计到达时间',
// english_name: 'estimated_arrival_time',
// type: 'VARCHAR(10)',
// length: '10',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '实际到达时间',
// english_name: 'actual_arrival_time',
// type: 'VARCHAR(10)',
// length: '10',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '快递反馈',
// english_name: 'courier_feedback',
// type: 'VARCHAR(6)',
// length: '6',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '是否退货',
// english_name: 'is_returned',
// type: 'VARCHAR(3)',
// length: '3',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '退款原因',
// english_name: 'refund_reason',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '客户满意度',
// english_name: 'customer_satisfaction',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '出生日期',
// english_name: 'date_of_birth',
// type: 'VARCHAR(17)',
// length: '17',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '品类',
// english_name: 'category',
// type: 'VARCHAR(9)',
// length: '9',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// {
// name: '材质',
// english_name: 'material',
// type: 'VARCHAR(6)',
// length: '6',
// point: '',
// null: '允许',
// default: '',
// type_name: '字符串',
// },
// ],
// })
return httpRequest.get('/api/resource/bi/v1/data/my/field-detail') return httpRequest.get('/api/resource/bi/v1/data/my/field-detail')
} }
// 进度查询
export function getProcessProgress(params: { function_name: string }) {
return httpRequest.get('/api/resource/bi/v1/processing/processing/progress', { params })
}
// 我的数据集列表 // 我的数据集列表
export function getChartList(params?: Partial<{ page: number; 'per-page': number }>) { export function getChartList(params?: Partial<{ page: number; 'per-page': number }>) {
return Promise.resolve({ return Promise.resolve({
......
import httpRequest from '@/utils/axios'
// 我的数据集列表
export function getMyList(params?: Partial<{ page: number; 'per-page': number }>) {
return httpRequest.get('/api/resource/bi/v1/data/my/list', { params })
}
// 查看字段详情
export function getMyField() {
return httpRequest.get('/api/resource/bi/v1/data/my/field-detail')
}
// 进度查询
export function getProcessProgress(params: { function_name: string }) {
return httpRequest.get('/api/resource/bi/v1/processing/processing/progress', { params })
}
// 数据可视化组件列表
export function getComponentList(params?: Partial<{ page: number; 'per-page': number; type: string }>) {
return httpRequest.get('/api/resource/bi/v1/reporting/component/list', { params })
}
// 更新数据可视化组件
export function updateComponent(data: { id: string; name: string; type: string; content: string }) {
return httpRequest.post('/api/resource/bi/v1/reporting/component/list', data)
}
// 删除数据可视化组件
export function deleteComponent(data: { id: string }) {
return httpRequest.post('/api/resource/bi/v1/reporting/component/list', data)
}
// 数据可视化组件详情
export function getComponent(params: { id: string }) {
return httpRequest.get('/api/resource/bi/v1/reporting/component/list', { params })
}
import { Button, Card, Flex } from 'antd' import { Button, Card, Flex } from 'antd'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import AppList, { AppListProps } from '@/components/AppList' import AppList, { AppListProps } from '@/components/AppList'
import { getChartList } from '@/api/base' import ViewDataButtonModal from '../data/ViewMyDataButtonModal'
import { getComponentList } from '@/api/data'
export default function DataWrap({ title, buttons }: { title: string; buttons: ReactNode; children?: ReactNode }) { export default function DataWrap({ title, buttons }: { title: string; buttons: ReactNode; children?: ReactNode }) {
const listOptions: AppListProps = { const listOptions: AppListProps = {
fetchApi: async (params) => { fetchApi: async (params) => {
const { data } = await getChartList(params) const { data } = await getComponentList(params)
return { ...data } return { ...data }
}, },
columns: [ columns: [
...@@ -54,7 +55,7 @@ export default function DataWrap({ title, buttons }: { title: string; buttons: R ...@@ -54,7 +55,7 @@ export default function DataWrap({ title, buttons }: { title: string; buttons: R
<Flex wrap gap={10}> <Flex wrap gap={10}>
{buttons} {buttons}
</Flex> </Flex>
<Button>查看我的数据集</Button> <ViewDataButtonModal></ViewDataButtonModal>
</Flex> </Flex>
<AppList {...listOptions} /> <AppList {...listOptions} />
</Card> </Card>
......
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Flex, Modal } from 'antd' import { Button, Flex, Modal } from 'antd'
import axios from 'axios'
import DataRender from './DataRender'
import { read, utils } from 'xlsx' import { read, utils } from 'xlsx'
import { uniqueId } from 'lodash-es' import { uniqueId } from 'lodash-es'
import axios from 'axios'
import DataRender from './DataRender'
export default function ViewDataButtonModal({ data }: { data: any }) { export default function ViewDataButtonModal({ data }: { data: any }) {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
......
import { useState } from 'react'
import { Button, Flex, Modal } from 'antd'
import { useDataQuery } from '@/hooks/useQuery'
import DataRender from './DataRender'
export default function ViewDataButtonModal() {
const [open, setOpen] = useState(false)
const { data, isPending } = useDataQuery()
const columns: any = data.title.map((item: any) => {
return {
title: item.name,
dataIndex: item.english_name,
align: 'center',
minWidth: 120,
}
})
return (
<>
<Button onClick={() => setOpen(true)}>查看我的数据集</Button>
<Modal title="查看我的数据集" width={'80%'} open={open} footer={null} onCancel={() => setOpen(false)}>
<Flex justify="space-between" style={{ marginBottom: 20 }}>
<div>数据集名称:{data.info?.name}</div>
<div>共计:{data.total}条数据</div>
</Flex>
<DataRender rowKey={'pk_id'} loading={isPending} dataSource={data.list} columns={columns} />
</Modal>
</>
)
}
import { useEffect } from 'react' import { useEffect, useState } from 'react'
import { useQuery } from '@tanstack/react-query' import { useQuery } from '@tanstack/react-query'
import { getUser, getMapList, getMyList, getMyField } from '@/api/base' import { getUser, getMapList, getMyList, getMyField, getProcessProgress } from '@/api/base'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useMapStore } from '@/stores/map' import { useMapStore } from '@/stores/map'
import axios from 'axios' import axios from 'axios'
import { read, utils } from 'xlsx' import { read, utils } from 'xlsx'
// 用户信息
export function useUserQuery() { export function useUserQuery() {
const { setUser } = useUserStore() const { setUser } = useUserStore()
...@@ -20,6 +21,7 @@ export function useUserQuery() { ...@@ -20,6 +21,7 @@ export function useUserQuery() {
return query return query
} }
// 字典
export function useMapQuery() { export function useMapQuery() {
const { setMap } = useMapStore() const { setMap } = useMapStore()
...@@ -53,6 +55,7 @@ export function useDataQuery() { ...@@ -53,6 +55,7 @@ export function useDataQuery() {
return query return query
} }
// 读取excel文件
export function useExcelQuery(url: string) { export function useExcelQuery(url: string) {
const query = useQuery({ const query = useQuery({
queryKey: ['excel', url], queryKey: ['excel', url],
...@@ -80,6 +83,7 @@ interface DataField { ...@@ -80,6 +83,7 @@ interface DataField {
type: string type: string
} }
// 字段
export function useDataFieldQuery() { export function useDataFieldQuery() {
const query = useQuery({ const query = useQuery({
queryKey: ['dataFiled'], queryKey: ['dataFiled'],
...@@ -89,6 +93,51 @@ export function useDataFieldQuery() { ...@@ -89,6 +93,51 @@ export function useDataFieldQuery() {
return { data: [] } return { data: [] }
}, },
}) })
const fields =
query.data?.map((item) => {
return { ...item, label: item.name, value: item.english_name }
}) || []
return query const getFieldName = (value: string) => {
return fields.find((item) => item.value === value)?.label || value
}
const getFieldNames = (values: string[]) => {
return values.map((value) => getFieldName(value))
}
return { ...query, fields, fieldOptions: fields, getFieldName, getFieldNames }
}
// 进度查询
export function useProcessProgressQuery(params: { function_name: string }) {
const [enabled, setEnabled] = useState(false)
const query = useQuery({
queryKey: ['processProgress', params],
queryFn: () => {
return getProcessProgress(params)
},
select: (res) => res.data,
enabled,
refetchInterval: enabled ? 1000 : false,
})
// 开始轮询的方法
const start = () => {
setEnabled(true)
}
// 结束轮询的方法
const stop = () => {
setEnabled(false)
}
// 组件卸载时清理
useEffect(() => {
return () => {
stop()
}
}, [])
return { ...query, start, stop }
} }
import { lazy } from 'react' import { lazy } from 'react'
import ChartWrap from '@/components/data/ChartWrap' import ChartWrap from '@/components/chart/ChartWrap'
const ButtonModal = lazy(() => import('../components/ButtonModal')) const ButtonModal = lazy(() => import('../components/ButtonModal'))
......
import ChartWrap from '@/components/data/ChartWrap' import ChartWrap from '@/components/chart/ChartWrap'
import { Button } from 'antd' import { Button } from 'antd'
export default function DataProcess() { export default function DataProcess() {
......
import ChartWrap from '@/components/data/ChartWrap' import ChartWrap from '@/components/chart/ChartWrap'
import { Button } from 'antd' import { Button } from 'antd'
export default function DataProcess() { export default function DataProcess() {
......
import ChartWrap from '@/components/data/ChartWrap' import ChartWrap from '@/components/chart/ChartWrap'
import { Button } from 'antd' import { Button } from 'antd'
export default function DataProcess() { export default function DataProcess() {
......
...@@ -27,7 +27,7 @@ export default function ButtonModal() { ...@@ -27,7 +27,7 @@ export default function ButtonModal() {
}) })
} }
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
useEffect(() => { useEffect(() => {
...@@ -59,9 +59,9 @@ export default function ButtonModal() { ...@@ -59,9 +59,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择逻辑错误值字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择逻辑错误值字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
...@@ -27,7 +27,7 @@ export default function ButtonModal() { ...@@ -27,7 +27,7 @@ export default function ButtonModal() {
}) })
} }
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
useEffect(() => { useEffect(() => {
...@@ -59,9 +59,9 @@ export default function ButtonModal() { ...@@ -59,9 +59,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择过大值字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择过大值字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
...@@ -27,7 +27,7 @@ export default function ButtonModal() { ...@@ -27,7 +27,7 @@ export default function ButtonModal() {
}) })
} }
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
useEffect(() => { useEffect(() => {
...@@ -59,9 +59,9 @@ export default function ButtonModal() { ...@@ -59,9 +59,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择过小值字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择过小值字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 缺省值处理
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/pre-processing/missing', data)
}
...@@ -5,6 +5,22 @@ import AppProgressSteps from '@/components/AppProgressSteps' ...@@ -5,6 +5,22 @@ import AppProgressSteps from '@/components/AppProgressSteps'
import { useSearchParams } from 'react-router' import { useSearchParams } from 'react-router'
import { useAI } from '@/hooks/useAI' import { useAI } from '@/hooks/useAI'
import prompt from '@/utils/prompt' import prompt from '@/utils/prompt'
import { useProcessData } from '../query'
const actionOptions = [
{ label: '统一规则处理', value: '统一规则处理' },
{ label: '逐个配置规则处理', value: '逐个配置规则处理' },
]
const ruleOptions = [
{ label: '不处理', value: '不处理' },
{ label: 'AI智能填充', value: 'AI智能填充' },
{ label: '删除', value: '删除' },
{ label: '平均值填充', value: '平均值填充' },
{ label: '特殊值填充', value: '特殊值填充' },
{ label: '热卡填充(上)', value: '热卡填充(上)' },
{ label: '热卡填充(下)', value: '热卡填充(下)' },
]
export default function ButtonModal() { export default function ButtonModal() {
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
...@@ -14,13 +30,12 @@ export default function ButtonModal() { ...@@ -14,13 +30,12 @@ export default function ButtonModal() {
try { try {
const parse = JSON.parse(message.content) const parse = JSON.parse(message.content)
console.log(parse) console.log(parse)
if (parse.results.length) form.setFieldValue('checkedList', parse.results.map((item: any) => item.name) || []) if (parse.results.length) form.setFieldValue('fields', parse.results.map((item: any) => item.name) || [])
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
}, },
}) })
const handleSearch = () => { const handleSearch = () => {
post({ post({
response_format: { type: 'json_object' }, response_format: { type: 'json_object' },
...@@ -28,7 +43,7 @@ export default function ButtonModal() { ...@@ -28,7 +43,7 @@ export default function ButtonModal() {
}) })
} }
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions, getFieldName, getFieldNames } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
useEffect(() => { useEffect(() => {
...@@ -36,46 +51,48 @@ export default function ButtonModal() { ...@@ -36,46 +51,48 @@ export default function ButtonModal() {
setOpen(true) setOpen(true)
} }
}, [searchParams]) }, [searchParams])
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
const [form] = Form.useForm() const [form] = Form.useForm()
const initialValues = { const initialValues = {
checkedList: searchParams.get('results')?.split(',') || [], fields: searchParams.get('results')?.split(',') || [],
method: 'unified', action: '统一规则处理',
rule: '1', rule: '不处理',
} }
const checkedList: string[] = Form.useWatch('checkedList', form) || [] const fields: string[] = Form.useWatch('fields', form) || []
const method = Form.useWatch('method', form) const action = Form.useWatch('action', form)
const rule = Form.useWatch('rule', form) const rule = Form.useWatch('rule', form)
const rules = Form.useWatch('rules', form) || {} const rules = Form.useWatch('rules', form) || {}
const methodOptions = [ const { mutate, isPending, progress, message } = useProcessData()
{ label: '统一规则处理', value: 'unified' }, // 开始处理
{ label: '逐个配置规则处理', value: 'individual' }, const handleStart = () => {
] form.validateFields().then((values) => {
const params = { ...values, fields: values.fields.join(','), rules: JSON.stringify(values.rules) }
const ruleOptions = [ mutate(params, {
{ label: '不处理', value: '1' }, onSuccess: handleClose,
{ label: 'AI智能填充', value: '2' }, })
{ label: '删除', value: '3' }, })
{ label: '平均值填充', value: '4' }, }
{ label: '特殊值填充', value: '5' },
{ label: '热卡填充(上)', value: '6' },
{ label: '热卡填充(下)', value: '7' },
]
const [step, setStep] = useState<number>(-1) // 关闭并重置
const handleClose = () => {
setOpen(false)
setCurrent(0)
form.resetFields()
}
const steps = [ const steps = [
{ {
title: '请选择缺失值字段', title: '请选择缺失值字段',
content: ( content: (
<Form.Item name="checkedList"> <Form.Item name="fields">
<Checkbox.Group> <Checkbox.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Checkbox value={item.name}>{item.name}</Checkbox> <Checkbox value={item.value}>{item.label}</Checkbox>
</Col> </Col>
))} ))}
</Row> </Row>
...@@ -87,18 +104,17 @@ export default function ButtonModal() { ...@@ -87,18 +104,17 @@ export default function ButtonModal() {
title: '配置处理规则', title: '配置处理规则',
content: ( content: (
<> <>
<Form.Item label="缺失值字段处理方法" name="method"> <Form.Item label="缺失值字段处理方法" name="action">
<Radio.Group options={methodOptions} /> <Radio.Group options={actionOptions} />
</Form.Item> </Form.Item>
{/* 统一规则处理 */} {action === '统一规则处理' && (
{method === 'unified' && (
<> <>
<Form.Item label="缺失值字段处理规则" name="rule"> <Form.Item label="缺失值字段处理规则" name="rule">
<Select options={ruleOptions} /> <Select options={ruleOptions} />
</Form.Item> </Form.Item>
{rule === '5' && ( {rule === '特殊值填充' && (
<Form.Item label="请填写特殊值" name="special"> <Form.Item label="请填写特殊值" name="special">
<Input placeholder="请输入" /> <Input placeholder="请输入" />
</Form.Item> </Form.Item>
...@@ -106,16 +122,17 @@ export default function ButtonModal() { ...@@ -106,16 +122,17 @@ export default function ButtonModal() {
</> </>
)} )}
{/* 逐个配置规则处理 */} {action === '逐个配置规则处理' &&
{method === 'individual' && fields.map((field, index) => (
checkedList.map((field) => (
<div key={field} style={{ marginBottom: 10 }}> <div key={field} style={{ marginBottom: 10 }}>
<Form.Item label="字段">{field}</Form.Item> <Form.Item label="字段" name={['rules', index, 'field']} initialValue={field}>
<Form.Item label="缺失值字段处理规则" name={['rules', field, 'rule']}> <span>{getFieldName(field)}</span>
</Form.Item>
<Form.Item label="缺失值字段处理规则" name={['rules', index, 'rule']}>
<Select options={ruleOptions} /> <Select options={ruleOptions} />
</Form.Item> </Form.Item>
{rules[field]?.rule === '5' && ( {rules[index]?.rule === '特殊值填充' && (
<Form.Item label="请填写特殊值" name={['rules', field, 'special']}> <Form.Item label="请填写特殊值" name={['rules', index, 'special']}>
<Input placeholder="请输入" /> <Input placeholder="请输入" />
</Form.Item> </Form.Item>
)} )}
...@@ -129,14 +146,14 @@ export default function ButtonModal() { ...@@ -129,14 +146,14 @@ export default function ButtonModal() {
title: '处理执行', title: '处理执行',
content: ( content: (
<> <>
<p>缺失值处理字段:{checkedList.join(', ')}</p> <p>缺失值处理字段:{getFieldNames(fields).join('、')}</p>
<Flex vertical align="center" style={{ marginTop: '20px' }}> <Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={() => setStep(1)}> <Button type="primary" loading={isPending} onClick={handleStart}>
开始处理 开始处理
</Button> </Button>
<AppProgressSteps <AppProgressSteps
style={{ margin: '80px' }} style={{ margin: '80px' }}
current={step} current={progress}
items={[ items={[
{ {
title: ( title: (
...@@ -164,13 +181,7 @@ export default function ButtonModal() { ...@@ -164,13 +181,7 @@ export default function ButtonModal() {
处理结果 处理结果
</> </>
), ),
description: ( description: <>{message[3]}</>,
<>
累计处理XX个字段
<br />
累计处理XX条记录
</>
),
}, },
]} ]}
/> />
...@@ -197,12 +208,12 @@ export default function ButtonModal() { ...@@ -197,12 +208,12 @@ export default function ButtonModal() {
)} )}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>} {current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && ( {current < steps.length - 1 && (
<Button type="primary" onClick={() => setCurrent(current + 1)} disabled={!checkedList.length}> <Button type="primary" onClick={() => setCurrent(current + 1)} disabled={!fields.length}>
下一步 下一步
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {current === steps.length - 1 && (
<Button type="primary" onClick={() => setOpen(false)}> <Button type="primary" disabled={isPending} onClick={handleClose}>
关闭 关闭
</Button> </Button>
)} )}
...@@ -210,7 +221,7 @@ export default function ButtonModal() { ...@@ -210,7 +221,7 @@ export default function ButtonModal() {
} }
destroyOnClose destroyOnClose
width={800} width={800}
onCancel={() => setOpen(false)}> onCancel={handleClose}>
<div style={{ minHeight: 300, padding: '20px 0' }}> <div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} labelCol={{ span: 5 }} preserve={false} initialValues={initialValues}> <Form form={form} labelCol={{ span: 5 }} preserve={false} initialValues={initialValues}>
{steps.map((item, index) => ( {steps.map((item, index) => (
......
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'missing' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
fields: string
action: string
rules: string
rule: string
specify: string
}
...@@ -24,7 +24,7 @@ export default function ButtonModal() { ...@@ -24,7 +24,7 @@ export default function ButtonModal() {
}) })
} }
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
...@@ -53,9 +53,9 @@ export default function ButtonModal() { ...@@ -53,9 +53,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择数据去空格字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择数据去空格字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
...@@ -4,7 +4,7 @@ import { useDataFieldQuery } from '@/hooks/useQuery' ...@@ -4,7 +4,7 @@ import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps' import AppProgressSteps from '@/components/AppProgressSteps'
export default function ButtonModal() { export default function ButtonModal() {
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
...@@ -34,9 +34,9 @@ export default function ButtonModal() { ...@@ -34,9 +34,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择数据拆分字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择数据拆分字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
...@@ -4,7 +4,7 @@ import { useDataFieldQuery } from '@/hooks/useQuery' ...@@ -4,7 +4,7 @@ import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps' import AppProgressSteps from '@/components/AppProgressSteps'
export default function ButtonModal() { export default function ButtonModal() {
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
...@@ -26,9 +26,9 @@ export default function ButtonModal() { ...@@ -26,9 +26,9 @@ export default function ButtonModal() {
<Form.Item name="checked" rules={[{ required: true, message: '请选择去标点字段' }]}> <Form.Item name="checked" rules={[{ required: true, message: '请选择去标点字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={6} key={item.english_name}> <Col span={6} key={item.value}>
<Radio value={item.name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
......
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 数据分箱
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/processing/binning', data)
}
import { useEffect, useState } from 'react' import { useState } from 'react'
import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Table, message } from 'antd' import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Checkbox } from 'antd'
import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery' import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps' import AppProgressSteps from '@/components/AppProgressSteps'
import { uniqBy } from 'lodash-es' import { useProcessData } from '../query'
import { MinusCircleOutlined } from '@ant-design/icons'
export default function ButtonModal() { export default function ButtonModal() {
const { data = { list: [] } } = useDataQuery() // 添加默认值防止 data 为 undefined const { fieldOptions, getFieldName } = useDataFieldQuery()
const { data: fields = [] } = useDataFieldQuery() // 数字类型字段
const currentFieldOptions = fieldOptions.filter(
(item) => item.type.includes('DECIMAL') || item.type.includes('SMALLINT')
)
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
const [form] = Form.useForm() const [form] = Form.useForm()
const [dataSource, setDataSource] = useState<{ key: number; raw_value: string; mapping_value: string }[]>([])
const [step, setStep] = useState(-1)
// 使用 Form.useWatch 监听表单值变化 const field = Form.useWatch('field', form)
const checked = Form.useWatch('checked', form) const action = Form.useWatch('action', form)
const rules = Form.useWatch('rules', form) || []
// 当 checked 或 data 变化时更新数据源 // 处理下一步按钮逻辑
useEffect(() => { const handleNext = async () => {
if (checked && data.list?.length) { if (current === 0) {
const uniqList = uniqBy(data.list, checked) // 第一步验证表单
setDataSource( await form.validateFields(['name', 'english_name', 'field'])
uniqList.map((item: any, index) => ({ } else if (current === 1) {
key: index + 1, await form.validateFields()
raw_value: item[checked] || '',
mapping_value: '',
}))
)
} }
}, [checked, data.list]) setCurrent(current + 1)
// 处理映射值变化
const handleMappingValueChange = (value: string, key: number) => {
setDataSource((prevDataSource) =>
prevDataSource.map((item) => (item.key === key ? { ...item, mapping_value: value } : item))
)
} }
const { mutate, isPending, progress, message } = useProcessData()
// 开始处理
const handleStart = () => { const handleStart = () => {
setStep(0) const values = form.getFieldsValue()
// Simulate processing steps with timeouts const params = { ...values, rules: JSON.stringify(values.rules), action: '固定步长分箱' }
setTimeout(() => { mutate(params, {
setStep(1) onSuccess: handleClose,
setTimeout(() => { })
setStep(4) }
message.success('数据处理完成!')
}, 1000) // 关闭并重置
}, 1000) const handleClose = () => {
setOpen(false)
setCurrent(0)
} }
const [groupName, setGroupName] = useState('')
// 步骤定义 // 步骤定义
const steps = [ const steps = [
{ {
...@@ -67,13 +67,13 @@ export default function ButtonModal() { ...@@ -67,13 +67,13 @@ export default function ButtonModal() {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label="请选择数据分箱字段" label="请选择数据分箱字段"
name="checked" name="field"
rules={[{ required: true, message: '请选择数据分箱字段' }]}> rules={[{ required: true, message: '请选择数据分箱字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {currentFieldOptions.map((item) => (
<Col span={8} key={item.english_name}> <Col span={8} key={item.value}>
<Radio value={item.english_name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
...@@ -86,30 +86,86 @@ export default function ButtonModal() { ...@@ -86,30 +86,86 @@ export default function ButtonModal() {
title: '配置数据分箱规则', title: '配置数据分箱规则',
content: ( content: (
<> <>
<Form.Item label="数据分箱操作字段">{checked}</Form.Item> <Form.Item label="数据分箱操作字段">{getFieldName(field)}</Form.Item>
<Form.Item> <Form.Item name="action">
<Table <Radio.Group block options={['固定步长分箱', '自定义刻度值分箱']} optionType="button" buttonStyle="solid" />
bordered
pagination={{ pageSize: 10 }}
columns={[
{ title: '序号', dataIndex: 'key', align: 'center', width: 80 },
{ title: '原始值', dataIndex: 'raw_value', align: 'center' },
{
title: '映射值',
dataIndex: 'mapping_value',
align: 'center',
render: (_, record) => (
<Input
value={record.mapping_value}
onChange={(e) => handleMappingValueChange(e.target.value, record.key)}
placeholder="请输入映射值"
/>
),
},
]}
dataSource={dataSource}
/>
</Form.Item> </Form.Item>
{action === '固定步长分箱' && (
<>
<Form.Item label="最小值">
<Form.Item name="min" rules={[{ required: true, message: '请输入最小值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>分箱的取值规则为:大于等于最小值。例如最小值为5,则为&gt;=5</p>
</Form.Item>
<Form.Item label="最大值">
<Form.Item name="max" rules={[{ required: true, message: '请输入最大值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>分箱的取值规则为:小于等于最大值。例如最大值为200,则为&lt;=200</p>
</Form.Item>
<Form.Item label="步长值">
<Form.Item name="step" rules={[{ required: true, message: '请输入步长值' }]} noStyle>
<Input placeholder="请输入" />
</Form.Item>
<p>以步长为分箱。例如步长值为100,则分箱结果为:5-100(含100),100-200(不含100)</p>
</Form.Item>
<Form.Item label="边界">
<Form.Item name="include_min" noStyle valuePropName="checked">
<Checkbox>包含小于最小值</Checkbox>
</Form.Item>
<Form.Item name="include_max" noStyle valuePropName="checked">
<Checkbox>包含大于最大值</Checkbox>
</Form.Item>
</Form.Item>
</>
)}
{action === '自定义刻度值分箱' && (
<>
<Form.List name="rules">
{(fields, { add, remove }) => (
<>
<Form.Item label="数据分组名称">
<Flex gap={10} align="center">
<Input
placeholder="请输入"
value={groupName}
onChange={(e) => setGroupName(e.target.value)}
style={{ width: '60%' }}
/>
<Button
type="primary"
onClick={() => {
if (!groupName) return
add(groupName) // 添加新分组
setGroupName('')
}}>
添加刻度
</Button>
</Flex>
</Form.Item>
{fields.map(({ key, name, ...restField }) => (
<Form.Item {...restField} label="刻度值" style={{ flex: 1 }} key={key}>
<Flex gap={10}>
<p style={{ width: '60%' }}>{rules[name]}</p>
<MinusCircleOutlined onClick={() => remove(name)} />
</Flex>
</Form.Item>
))}
</>
)}
</Form.List>
<Form.Item label="边界">
<Form.Item name="include_min" noStyle valuePropName="checked">
<Checkbox>包含小于最小值</Checkbox>
</Form.Item>
<Form.Item name="include_max" noStyle valuePropName="checked">
<Checkbox>包含大于最大值</Checkbox>
</Form.Item>
</Form.Item>
</>
)}
</> </>
), ),
}, },
...@@ -118,12 +174,12 @@ export default function ButtonModal() { ...@@ -118,12 +174,12 @@ export default function ButtonModal() {
content: ( content: (
<> <>
<Flex vertical align="center" style={{ marginTop: '20px' }}> <Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={handleStart}> <Button type="primary" onClick={handleStart} loading={isPending}>
开始处理 开始处理
</Button> </Button>
<AppProgressSteps <AppProgressSteps
style={{ margin: '80px' }} style={{ margin: '80px' }}
current={step} current={progress}
items={[ items={[
{ {
title: ( title: (
...@@ -160,13 +216,7 @@ export default function ButtonModal() { ...@@ -160,13 +216,7 @@ export default function ButtonModal() {
处理结果 处理结果
</> </>
), ),
description: ( description: <>{message[3]}</>,
<>
累计处理{dataSource.length}个字段
<br />
累计处理{data.list?.length || 0}条记录
</>
),
}, },
]} ]}
/> />
...@@ -176,29 +226,6 @@ export default function ButtonModal() { ...@@ -176,29 +226,6 @@ export default function ButtonModal() {
}, },
] ]
// 处理下一步按钮逻辑
const handleNext = async () => {
try {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name', 'checked'])
}
setCurrent(current + 1)
} catch (error) {
// 表单验证错误处理
console.error('表单验证失败', error)
}
}
// 重置状态
const handleClose = () => {
setOpen(false)
setCurrent(0)
setStep(-1)
form.resetFields()
setDataSource([])
}
return ( return (
<> <>
<Button type="primary" onClick={() => setOpen(true)}> <Button type="primary" onClick={() => setOpen(true)}>
...@@ -217,7 +244,7 @@ export default function ButtonModal() { ...@@ -217,7 +244,7 @@ export default function ButtonModal() {
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {current === steps.length - 1 && (
<Button type="primary" onClick={handleClose}> <Button type="primary" onClick={handleClose} disabled={isPending}>
关闭 关闭
</Button> </Button>
)} )}
...@@ -227,7 +254,13 @@ export default function ButtonModal() { ...@@ -227,7 +254,13 @@ export default function ButtonModal() {
width={800} width={800}
onCancel={handleClose}> onCancel={handleClose}>
<div style={{ minHeight: 300, padding: '20px 0' }}> <div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} labelCol={{ span: 5 }} preserve={false}> <Form
form={form}
labelCol={{ span: 5 }}
preserve={false}
initialValues={{
action: '固定步长分箱',
}}>
{steps.map((item, index) => ( {steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}> <div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content} {item.content}
......
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'mapping' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
name: string
english_name: string
field: string
rule: string
action: string
}
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 数据分箱
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/processing/anonymization', data)
}
import { useState } from 'react'
import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Space, Select } from 'antd'
import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
const functionShortcutOptions = [
{
label: '获取“年份”信息',
value: 'YEAR',
demo: '如:“2025年”',
},
// {
// label: '获取“季度”信息',
// value: 'YEAR',
// demo: '如:“一季度”',
// },
// {
// label: '获取“年季度”信息',
// value: 'YEAR',
// demo: '如:“2025年一季度”',
// },
{
label: '获取“月份”信息',
value: 'MONTH',
demo: '如:“3月”',
},
{
label: '获取“年月份”信息',
value: 'DATE',
demo: '如:“2025年3月”',
},
{
label: '获取“周”信息',
value: 'NETWORKDAYS',
demo: '如:“12周”',
},
// {
// label: '获取“年周”信息',
// value: 'YEAR',
// demo: '如:“2025年12周”',
// },
{
label: '获取“日”信息',
value: 'DAY',
demo: '如:“21日”',
},
{
label: '获取“小时”信息',
value: 'HOUR',
demo: '如:“12时”',
},
{
label: '获取“分钟”信息',
value: 'MINUTE',
demo: '如:“21分”',
},
{
label: '获取“秒”信息',
value: 'SECOND',
demo: '如:“18秒”',
},
]
const functionOptions = [
{ label: 'DATE:组合年、月、日为标准日期', value: 'DATE' },
{ label: 'DATEDIF:计算两个日期间的年/月/日差值', value: 'DATEDIF' },
{ label: 'DATEVALUE:将文本日期转换为 Excel 可识别的序列号', value: 'DATEVALUE' },
{ label: 'DAY:提取日期中的“日”部分', value: 'DAY' },
{ label: 'EDATE:返回指定日期之前/之后几个月的日期', value: 'EDATE' },
{ label: 'EMONTH:返回指定日期之前/之后几个月的最后一天', value: 'EMONTH' },
{ label: 'HOUR:提取时间中的“小时”部分', value: 'HOUR' },
{ label: 'MINUTE:提取时间中的“分钟”部分', value: 'MINUTE' },
{ label: 'MONTH:提取日期中的“月”部分', value: 'MONTH' },
{ label: 'NETWORKDAYS:计算两个日期之间的工作日天数(排除周末和节假日)', value: 'NETWORKDAYS' },
{ label: 'NOW:返回当前日期和时间(精确到秒)', value: 'NOW' },
{ label: 'SECOND:提取时间中的“秒”部分', value: 'SECOND' },
{ label: 'TIME:组合时、分、秒为标准时间', value: 'TIME' },
{ label: 'TIMEVALUE:将文本时间转换为小数', value: 'TIMEVALUE' },
{ label: 'TODAY:返回当前系统日期(无参数)', value: 'TODAY' },
{ label: 'WEEKDAY:返回日期对应的星期几(数字形式)', value: 'WEEKDAY' },
{ label: 'WORKDAY:计算指定工作日天数后的日期(跳过周末和节假日)', value: 'WORKDAY' },
{ label: 'YEAR:提取日期中的“年”部分', value: 'YEAR' },
]
export default function ButtonModal() {
const { fieldOptions, getFieldName } = useDataFieldQuery()
const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const action = Form.useWatch('action', form)
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name'])
} else if (current === 1) {
await form.validateFields()
}
setCurrent(current + 1)
}
const { mutate, isPending, progress, message } = useProcessData()
// 开始处理
const handleStart = () => {
const values = form.getFieldsValue()
const params = { ...values, rules: JSON.stringify(values.rules), action: '固定步长分箱' }
mutate(params, {
onSuccess: handleClose,
})
}
// 关闭并重置
const handleClose = () => {
setOpen(false)
setCurrent(0)
}
// 步骤定义
const steps = [
{
title: '请选择日期计算字段',
content: (
<>
<Form.Item label="日期计算字段名称" name="name" rules={[{ required: true, message: '请输入字段名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="日期计算字段英文名称"
name="english_name"
rules={[{ required: true, message: '请输入英文名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
),
},
{
title: '配置日期计算规则',
content: (
<>
<Form.Item label="请选择日期计算方法" name="action">
<Radio.Group options={['快速日期计算', '日期函数计算']} />
</Form.Item>
{action === '快速日期计算' && (
<>
<Form.Item label="请选择快速日期计算方法" name="function">
<Radio.Group style={{ width: '100%' }}>
{functionShortcutOptions.map((item) => (
<Flex>
<Radio value={item.value}>
<Space key={item.value}>
<p style={{ minWidth: 200 }}>{item.label}</p>
<p style={{ color: '#999' }}>{item.demo}</p>
</Space>
</Radio>
</Flex>
))}
</Radio.Group>
</Form.Item>
</>
)}
{action === '日期函数计算' && (
<>
<Form.Item label="请选择函数" name="function">
<Select options={functionOptions} />
</Form.Item>
<Form.Item label="请输入函数公式">
<Flex gap={10}>
<Form.Item name="content" noStyle>
<Input.TextArea rows={4} />
</Form.Item>
<Button type="primary" style={{ height: 100 }}>
添加字段
</Button>
</Flex>
</Form.Item>
</>
)}
</>
),
},
{
title: '处理执行',
content: (
<>
<Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={handleStart} loading={isPending}>
开始处理
</Button>
<AppProgressSteps
style={{ margin: '80px' }}
current={progress}
items={[
{
title: (
<>
第一步
<br />
新建字段
</>
),
},
{
title: (
<>
第二步
<br />
复制数据
</>
),
},
{
title: (
<>
第三步
<br />
日期计算处理
</>
),
},
{
title: (
<>
第四步
<br />
处理结果
</>
),
description: <>{message[3]}</>,
},
]}
/>
</Flex>
</>
),
},
]
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
日期计算
</Button>
<Modal
title={steps[current]?.title}
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && action === '日期函数计算' && <Button type="primary">AI智能建议</Button>}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
下一步
</Button>
)}
{current === steps.length - 1 && (
<Button type="primary" onClick={handleClose} disabled={isPending}>
关闭
</Button>
)}
</Flex>
}
destroyOnClose
width={800}
onCancel={handleClose}>
<div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} labelCol={{ span: 5 }} preserve={false} initialValues={{ action: '快速日期计算' }}>
{steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content}
</div>
))}
</Form>
</div>
</Modal>
</>
)
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'anonymization' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
name: string
english_name: string
field: string
action: string
function: string
char?: string
sm_fun?: string
start?: string
end?: string
}
import { lazy } from 'react'
import DataWrap from '@/components/data/DataWrap' import DataWrap from '@/components/data/DataWrap'
import { Button } from 'antd'
const ButtonModal = lazy(() => import('../components/ButtonModal'))
export default function DataProcess() { export default function DataProcess() {
return <DataWrap title="数据加工:日期计算" buttons={<Button type="primary">日期计算</Button>}></DataWrap> return <DataWrap title="数据加工:日期计算" buttons={<ButtonModal />}></DataWrap>
} }
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 数据分箱
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/processing/anonymization', data)
}
import { useState } from 'react'
import { Button, Flex, Modal, Radio, Input, Form, Row, Col } from 'antd'
import { useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
export default function ButtonModal() {
const { fieldOptions, getFieldName } = useDataFieldQuery()
const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const field = Form.useWatch('field', form)
const action = Form.useWatch('action', form)
const formFunction = Form.useWatch('function', form)
const formSmFun = Form.useWatch('sm_fun', form)
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name', 'field'])
} else if (current === 1) {
await form.validateFields()
}
setCurrent(current + 1)
}
const { mutate, isPending, progress, message } = useProcessData()
// 开始处理
const handleStart = () => {
const values = form.getFieldsValue()
const params = { ...values, rules: JSON.stringify(values.rules), action: '固定步长分箱' }
mutate(params, {
onSuccess: handleClose,
})
}
// 关闭并重置
const handleClose = () => {
setOpen(false)
setCurrent(0)
}
// 步骤定义
const steps = [
{
title: '请选择数据脱敏字段',
content: (
<>
<Form.Item label="数据脱敏字段名称" name="name" rules={[{ required: true, message: '请输入字段名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="数据脱敏字段英文名称"
name="english_name"
rules={[{ required: true, message: '请输入英文名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="请选择数据脱敏字段"
name="field"
rules={[{ required: true, message: '请选择数据脱敏字段' }]}>
<Radio.Group>
<Row gutter={[10, 10]}>
{fieldOptions.map((item) => (
<Col span={8} key={item.value}>
<Radio value={item.value}>{item.label}</Radio>
</Col>
))}
</Row>
</Radio.Group>
</Form.Item>
</>
),
},
{
title: '配置数据脱敏规则',
content: (
<>
<Form.Item label="数据脱敏操作字段">{getFieldName(field)}</Form.Item>
<Form.Item name="action">
<Radio.Group
block
options={['基础脱敏方法', '高级脱敏方法', '特殊脱敏方法']}
optionType="button"
buttonStyle="solid"
/>
</Form.Item>
{action === '基础脱敏方法' && (
<>
<Form.Item label="请选择脱敏方法" name="function" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['固定遮蔽', '动态遮蔽', '内容替换', '内容模糊']} />
</Form.Item>
{formFunction === '固定遮蔽' && (
<>
<Form.Item label="请输入遮蔽符号" name="char" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入用于替换的固定符号,如:*、X等" />
</Form.Item>
<Form.Item label="请选择遮蔽范围" name="sm_fun" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['部分遮蔽', '全部遮蔽']} />
</Form.Item>
{formSmFun === '部分遮蔽' && (
<>
<Form.Item label="请输入开始位数" name="start" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="请输入结束位数" name="end" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
</>
)}
{formFunction === '动态遮蔽' && (
<>
<Form.Item label="请输入遮蔽符号" name="char" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入用于替换的固定符号,如:*、X等" />
</Form.Item>
<Form.Item label="请输入开始动态位数" name="start" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="请输入结束动态位数" name="end" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
{formFunction === '内容替换' && (
<>
<Form.Item label="请输入替换内容" name="char" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
{formFunction === '内容模糊' && (
<>
<Form.Item label="请输入模糊内容" name="char" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
</>
)}
{action === '高级脱敏方法' && (
<>
<Form.Item label="请选择脱敏方法" name="function" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['伪名化', '数据扰动', '空值化', '数据组合']} />
</Form.Item>
{formFunction === '伪名化' && (
<>
<Form.Item label="请选择伪名化方法" name="sm_fun" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['AI智能']} />
</Form.Item>
</>
)}
{formFunction === '数据扰动' && (
<>
<Form.Item label="请选择伪名化方法" name="sm_fun" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['AI智能']} />
</Form.Item>
</>
)}
{formFunction === '数据组合' && (
<>
<Form.Item label="请选择数据组合方法" name="sm_fun" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['添加前缀', '添加后缀']} />
</Form.Item>
<Form.Item name="char" rules={[{ required: true, message: '请输入' }]}>
<Input placeholder="请输入" />
</Form.Item>
</>
)}
</>
)}
{action === '特殊脱敏方法' && (
<>
<Form.Item label="请选择脱敏方法" name="function" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['数据加密']} />
</Form.Item>
<Form.Item label="请选择数据加密方法" name="sm_fun" rules={[{ required: true, message: '请选择' }]}>
<Radio.Group options={['AES', 'RSA']} />
</Form.Item>
</>
)}
</>
),
},
{
title: '处理执行',
content: (
<>
<Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={handleStart} loading={isPending}>
开始处理
</Button>
<AppProgressSteps
style={{ margin: '80px' }}
current={progress}
items={[
{
title: (
<>
第一步
<br />
新建字段
</>
),
},
{
title: (
<>
第二步
<br />
复制数据
</>
),
},
{
title: (
<>
第三步
<br />
数据脱敏处理
</>
),
},
{
title: (
<>
第四步
<br />
处理结果
</>
),
description: <>{message[3]}</>,
},
]}
/>
</Flex>
</>
),
},
]
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
数据脱敏
</Button>
<Modal
title={steps[current]?.title}
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
下一步
</Button>
)}
{current === steps.length - 1 && (
<Button type="primary" onClick={handleClose} disabled={isPending}>
关闭
</Button>
)}
</Flex>
}
destroyOnClose
width={800}
onCancel={handleClose}>
<div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} labelCol={{ span: 5 }} preserve={false} initialValues={{ action: '基础脱敏方法' }}>
{steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content}
</div>
))}
</Form>
</div>
</Modal>
</>
)
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'anonymization' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
name: string
english_name: string
field: string
action: string
function: string
char?: string
sm_fun?: string
start?: string
end?: string
}
import { lazy } from 'react'
import DataWrap from '@/components/data/DataWrap' import DataWrap from '@/components/data/DataWrap'
import { Button } from 'antd'
const ButtonModal = lazy(() => import('../components/ButtonModal'))
export default function DataProcess() { export default function DataProcess() {
return <DataWrap title="数据加工:数据脱敏" buttons={<Button type="primary">数据脱敏</Button>}></DataWrap> return <DataWrap title="数据加工:数据脱敏" buttons={<ButtonModal />}></DataWrap>
} }
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 数据分箱
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/processing/grouping', data)
}
import { useEffect, useState } from 'react'
import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Select } from 'antd'
import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps'
import { useProcessData } from '../query'
import { MinusCircleOutlined } from '@ant-design/icons'
import { uniqBy } from 'lodash-es'
export default function ButtonModal() {
const { data = { list: [] } } = useDataQuery()
const { fieldOptions, getFieldName } = useDataFieldQuery()
// 数字类型字段
const currentFieldOptions = fieldOptions.filter((item) => item.type.includes('VARCHAR'))
const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0)
const [form] = Form.useForm()
const [dataSource, setDataSource] = useState<{ key: number; label: string; value: string }[]>([])
const field = Form.useWatch('field', form)
const rules = Form.useWatch('rules', form) || []
useEffect(() => {
if (field && data.list?.length) {
const uniqList = uniqBy(data.list, field)
setDataSource(
uniqList.map((item: any, index) => ({
key: index + 1,
label: item[field] || '',
value: item[field] || '',
}))
)
}
}, [field, data.list])
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name', 'field'])
} else if (current === 1) {
await form.validateFields()
}
setCurrent(current + 1)
}
const { mutate, isPending, progress, message } = useProcessData()
// 开始处理
const handleStart = () => {
const values = form.getFieldsValue()
const params = {
...values,
rules: JSON.stringify(
values.rules.map((item: any) => {
return {
...item,
fields: item.fields.join(','),
}
})
),
}
mutate(params, {
onSuccess: handleClose,
})
}
// 关闭并重置
const handleClose = () => {
setOpen(false)
setCurrent(0)
}
const [groupName, setGroupName] = useState('')
// 步骤定义
const steps = [
{
title: '请选择数据分组字段',
content: (
<>
<Form.Item label="数据分组字段名称" name="name" rules={[{ required: true, message: '请输入字段名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="数据分组字段英文名称"
name="english_name"
rules={[{ required: true, message: '请输入英文名称' }]}>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item
label="请选择数据分组字段"
name="field"
rules={[{ required: true, message: '请选择数据分组字段' }]}>
<Radio.Group>
<Row gutter={[10, 10]}>
{currentFieldOptions.map((item) => (
<Col span={8} key={item.value}>
<Radio value={item.value}>{item.label}</Radio>
</Col>
))}
</Row>
</Radio.Group>
</Form.Item>
</>
),
},
{
title: '配置数据分组规则',
content: (
<>
<Form.Item label="数据分组操作字段">{getFieldName(field)}</Form.Item>
<Form.List name="rules">
{(fields, { add, remove }) => (
<>
<Form.Item label="数据分组名称">
<Flex gap={10} align="center">
<Input
placeholder="请输入"
value={groupName}
onChange={(e) => setGroupName(e.target.value)}
style={{ width: '60%' }}
/>
<Button
type="primary"
onClick={() => {
if (!groupName) return
add({ name: groupName, fields: [] }) // 添加新分组
setGroupName('')
}}>
添加分组
</Button>
</Flex>
</Form.Item>
{fields.map(({ key, name, ...restField }) => (
<Form.Item {...restField} label={rules[name]?.name} style={{ flex: 1 }} key={key}>
<Flex gap={10}>
<Form.Item name={[name, 'fields']} noStyle>
<Select options={dataSource} mode="multiple" style={{ width: '60%' }} />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(name)} />
</Flex>
</Form.Item>
))}
</>
)}
</Form.List>
</>
),
},
{
title: '处理执行',
content: (
<>
<Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={handleStart} loading={isPending}>
开始处理
</Button>
<AppProgressSteps
style={{ margin: '80px' }}
current={progress}
items={[
{
title: (
<>
第一步
<br />
新建字段
</>
),
},
{
title: (
<>
第二步
<br />
复制数据
</>
),
},
{
title: (
<>
第三步
<br />
数据分组处理
</>
),
},
{
title: (
<>
第四步
<br />
处理结果
</>
),
description: <>{message[3]}</>,
},
]}
/>
</Flex>
</>
),
},
]
return (
<>
<Button type="primary" onClick={() => setOpen(true)}>
数据分组
</Button>
<Modal
title={steps[current]?.title}
open={open}
footer={
<Flex justify="center" gap={20}>
{current === 1 && <Button type="primary">AI智能建议</Button>}
{current > 0 && <Button onClick={() => setCurrent(current - 1)}>上一步</Button>}
{current < steps.length - 1 && (
<Button type="primary" onClick={handleNext}>
下一步
</Button>
)}
{current === steps.length - 1 && (
<Button type="primary" onClick={handleClose} disabled={isPending}>
关闭
</Button>
)}
</Flex>
}
destroyOnClose
width={800}
onCancel={handleClose}>
<div style={{ minHeight: 300, padding: '20px 0' }}>
<Form form={form} labelCol={{ span: 5 }} preserve={false}>
{steps.map((item, index) => (
<div key={index} hidden={current !== index} style={{ marginTop: '20px' }}>
{item.content}
</div>
))}
</Form>
</div>
</Modal>
</>
)
}
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'grouping' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
name: string
english_name: string
field: string
rules: string
}
import { lazy } from 'react'
import DataWrap from '@/components/data/DataWrap' import DataWrap from '@/components/data/DataWrap'
import { Button } from 'antd'
const ButtonModal = lazy(() => import('../components/ButtonModal'))
export default function DataProcess() { export default function DataProcess() {
return <DataWrap title="数据加工:数据分组" buttons={<Button type="primary">数据分组</Button>}></DataWrap> return <DataWrap title="数据加工:数据分组" buttons={<ButtonModal />}></DataWrap>
} }
import httpRequest from '@/utils/axios'
import type { ProcessDataParams } from './types'
// 值映射
export function processData(data: ProcessDataParams) {
return httpRequest.post('/api/resource/bi/v1/processing/processing/mapping', data)
}
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Table, message } from 'antd' import { Button, Flex, Modal, Radio, Input, Form, Row, Col, Table } from 'antd'
import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery' import { useDataQuery, useDataFieldQuery } from '@/hooks/useQuery'
import AppProgressSteps from '@/components/AppProgressSteps' import AppProgressSteps from '@/components/AppProgressSteps'
import { uniqBy } from 'lodash-es' import { uniqBy } from 'lodash-es'
import { useProcessData } from '../query'
export default function ButtonModal() { export default function ButtonModal() {
const { data = { list: [] } } = useDataQuery() // 添加默认值防止 data 为 undefined const { data = { list: [] } } = useDataQuery()
const { data: fields = [] } = useDataFieldQuery() const { fieldOptions, getFieldName } = useDataFieldQuery()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [current, setCurrent] = useState(0) const [current, setCurrent] = useState(0)
const [form] = Form.useForm() const [form] = Form.useForm()
const [dataSource, setDataSource] = useState<{ key: number; raw_value: string; mapping_value: string }[]>([]) const [dataSource, setDataSource] = useState<{ key: number; raw_value: string; new_value: string }[]>([])
const [step, setStep] = useState(-1)
// 使用 Form.useWatch 监听表单值变化 const field = Form.useWatch('field', form)
const checked = Form.useWatch('checked', form)
// 当 checked 或 data 变化时更新数据源
useEffect(() => { useEffect(() => {
if (checked && data.list?.length) { if (field && data.list?.length) {
const uniqList = uniqBy(data.list, checked) const uniqList = uniqBy(data.list, field)
setDataSource( setDataSource(
uniqList.map((item: any, index) => ({ uniqList.map((item: any, index) => ({
key: index + 1, key: index + 1,
raw_value: item[checked] || '', raw_value: item[field] || '',
mapping_value: '', new_value: '',
})) }))
) )
} }
}, [checked, data.list]) }, [field, data.list])
// 处理映射值变化 // 处理映射值变化
const handleMappingValueChange = (value: string, key: number) => { const handleValueChange = (value: string, key: number) => {
setDataSource((prevDataSource) => setDataSource((prevDataSource) =>
prevDataSource.map((item) => (item.key === key ? { ...item, mapping_value: value } : item)) prevDataSource.map((item) => (item.key === key ? { ...item, new_value: value } : item))
) )
} }
// 处理下一步按钮逻辑
const handleNext = async () => {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name', 'field'])
}
setCurrent(current + 1)
}
const { mutate, isPending, progress, message } = useProcessData()
// 开始处理
const handleStart = () => { const handleStart = () => {
setStep(0) const values = form.getFieldsValue()
// Simulate processing steps with timeouts const rule = dataSource.reduce((result, item) => {
setTimeout(() => { return { ...result, [item.raw_value]: item.new_value }
setStep(1) }, {})
setTimeout(() => { const params = { ...values, rule: JSON.stringify(rule) }
setStep(4) mutate(params, {
message.success('数据处理完成!') onSuccess: handleClose,
}, 1000) })
}, 1000) }
// 关闭并重置
const handleClose = () => {
setOpen(false)
setCurrent(0)
} }
// 步骤定义 // 步骤定义
...@@ -65,12 +80,12 @@ export default function ButtonModal() { ...@@ -65,12 +80,12 @@ export default function ButtonModal() {
rules={[{ required: true, message: '请输入英文名称' }]}> rules={[{ required: true, message: '请输入英文名称' }]}>
<Input placeholder="请输入" /> <Input placeholder="请输入" />
</Form.Item> </Form.Item>
<Form.Item label="请选择值映射字段" name="checked" rules={[{ required: true, message: '请选择值映射字段' }]}> <Form.Item label="请选择值映射字段" name="field" rules={[{ required: true, message: '请选择值映射字段' }]}>
<Radio.Group> <Radio.Group>
<Row gutter={[10, 10]}> <Row gutter={[10, 10]}>
{fields.map((item) => ( {fieldOptions.map((item) => (
<Col span={8} key={item.english_name}> <Col span={8} key={item.value}>
<Radio value={item.english_name}>{item.name}</Radio> <Radio value={item.value}>{item.label}</Radio>
</Col> </Col>
))} ))}
</Row> </Row>
...@@ -83,7 +98,7 @@ export default function ButtonModal() { ...@@ -83,7 +98,7 @@ export default function ButtonModal() {
title: '配置值映射规则', title: '配置值映射规则',
content: ( content: (
<> <>
<Form.Item label="值映射操作字段">{checked}</Form.Item> <Form.Item label="值映射操作字段">{getFieldName(field)}</Form.Item>
<Form.Item> <Form.Item>
<Table <Table
bordered bordered
...@@ -93,12 +108,12 @@ export default function ButtonModal() { ...@@ -93,12 +108,12 @@ export default function ButtonModal() {
{ title: '原始值', dataIndex: 'raw_value', align: 'center' }, { title: '原始值', dataIndex: 'raw_value', align: 'center' },
{ {
title: '映射值', title: '映射值',
dataIndex: 'mapping_value', dataIndex: 'new_value',
align: 'center', align: 'center',
render: (_, record) => ( render: (_, record) => (
<Input <Input
value={record.mapping_value} value={record.new_value}
onChange={(e) => handleMappingValueChange(e.target.value, record.key)} onChange={(e) => handleValueChange(e.target.value, record.key)}
placeholder="请输入映射值" placeholder="请输入映射值"
/> />
), ),
...@@ -115,12 +130,12 @@ export default function ButtonModal() { ...@@ -115,12 +130,12 @@ export default function ButtonModal() {
content: ( content: (
<> <>
<Flex vertical align="center" style={{ marginTop: '20px' }}> <Flex vertical align="center" style={{ marginTop: '20px' }}>
<Button type="primary" onClick={handleStart} disabled={step >= 0}> <Button type="primary" onClick={handleStart} loading={isPending}>
开始处理 开始处理
</Button> </Button>
<AppProgressSteps <AppProgressSteps
style={{ margin: '80px' }} style={{ margin: '80px' }}
current={step} current={progress}
items={[ items={[
{ {
title: ( title: (
...@@ -157,13 +172,7 @@ export default function ButtonModal() { ...@@ -157,13 +172,7 @@ export default function ButtonModal() {
处理结果 处理结果
</> </>
), ),
description: ( description: <>{message[3]}</>,
<>
累计处理{dataSource.length}个字段
<br />
累计处理{data.list?.length || 0}条记录
</>
),
}, },
]} ]}
/> />
...@@ -173,29 +182,6 @@ export default function ButtonModal() { ...@@ -173,29 +182,6 @@ export default function ButtonModal() {
}, },
] ]
// 处理下一步按钮逻辑
const handleNext = async () => {
try {
if (current === 0) {
// 第一步验证表单
await form.validateFields(['name', 'english_name', 'checked'])
}
setCurrent(current + 1)
} catch (error) {
// 表单验证错误处理
console.error('表单验证失败', error)
}
}
// 重置状态
const handleClose = () => {
setOpen(false)
setCurrent(0)
setStep(-1)
form.resetFields()
setDataSource([])
}
return ( return (
<> <>
<Button type="primary" onClick={() => setOpen(true)}> <Button type="primary" onClick={() => setOpen(true)}>
...@@ -214,7 +200,7 @@ export default function ButtonModal() { ...@@ -214,7 +200,7 @@ export default function ButtonModal() {
</Button> </Button>
)} )}
{current === steps.length - 1 && ( {current === steps.length - 1 && (
<Button type="primary" onClick={handleClose}> <Button type="primary" onClick={handleClose} disabled={isPending}>
关闭 关闭
</Button> </Button>
)} )}
......
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { message } from 'antd'
import { processData } from './api'
import type { ProcessDataParams } from './types'
import { useProcessProgressQuery } from '@/hooks/useQuery'
// 处理数据
export function useProcessData() {
const queryClient = useQueryClient()
const { data, start, stop } = useProcessProgressQuery({ function_name: 'mapping' })
const query = useMutation({
mutationFn: (data: ProcessDataParams) => {
start()
return processData(data)
},
onSuccess: () => {
stop()
message.success('处理完成')
queryClient.invalidateQueries({ queryKey: ['data'] })
},
})
return { ...query, progress: data?.progress ?? -1, message: data?.message ?? {} }
}
export interface ProcessDataParams {
name: string
english_name: string
field: string
rule: string
}
...@@ -47,13 +47,13 @@ axiosInstance.interceptors.response.use( ...@@ -47,13 +47,13 @@ axiosInstance.interceptors.response.use(
}, },
(error) => { (error) => {
if (error.response) { if (error.response) {
const { status, message } = error.response.data const { status, message: errorMessage } = error.response.data
// 未登录 // 未登录
if (status === 403) { if (status === 403) {
location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}` location.href = `${import.meta.env.VITE_LOGIN_URL}?rd=${encodeURIComponent(location.href)}`
} else { } else {
message.error(message || error.message) message.error(errorMessage || error.message)
console.error(`${status}: ${message}`) console.error(`${status}: ${errorMessage}`)
} }
} else { } else {
console.error(error) console.error(error)
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论