| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- import * as React from 'react';
- import { Form, Input, Select, Cascader, Tag, InputNumber, ColorPicker, Button, Space, message } from 'antd';
- import { PlusCircleOutlined, MinusCircleOutlined, CloseCircleOutlined, LinkOutlined } from '@ant-design/icons';
- import * as AllIcons from '@ant-design/icons';
- import IconPicker from './IconPicker';
- import VipSelector from './VipSelector';
- import './style.scss';
- interface Step1BasicProps {
- form: any;
- appTypeList: any[];
- appVisibleList: any[];
- appProjectList: any[];
- isAppPro: boolean;
- visibleFlag: string | number;
- vipList: any[];
- userInfo: any;
- onAppChange: (typeId: number) => void;
- onVisibleChange: (value: any) => void;
- onRemoveVip: (userId: string) => void;
- onVipConfirm: (users: any[]) => void;
- onNext: () => void;
- onBack: () => void;
- }
- interface InputItem {
- id: number;
- value: string;
- }
- const Step1Basic: React.FC<Step1BasicProps> = (props) => {
- const {
- form,
- appTypeList,
- appVisibleList,
- appProjectList,
- isAppPro,
- visibleFlag,
- vipList,
- userInfo,
- onAppChange,
- onVisibleChange,
- onAddVip,
- onRemoveVip,
- onNext,
- onBack,
- } = props;
- const [inputs, setInputs] = React.useState<InputItem[]>([{ id: 1, value: '' }]);
- const [iconPickerVisible, setIconPickerVisible] = React.useState(false);
- const [vipSelectorVisible, setVipSelectorVisible] = React.useState(false);
- const [selectedIcon, setSelectedIcon] = React.useState<string | null>(null);
- const [previewBg, setPreviewBg] = React.useState<string>('#005D80');
- const getContrastColor = (hex: string) => {
- const c = hex.replace('#', '');
- const r = parseInt(c.substring(0, 2), 16);
- const g = parseInt(c.substring(2, 4), 16);
- const b = parseInt(c.substring(4, 6), 16);
- const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
- return luminance > 0.6 ? '#000' : '#fff';
- };
- const presetColors = ['#1677ff', '#52c41a', '#fa8c16', '#f5222d', '#722ed1', '#ffffff', '#f0f0f0'];
- const presetItems = [{ label: '', colors: presetColors }];
- const addInput = () => {
- const newId = inputs.length + 1;
- setInputs([...inputs, { id: newId, value: '' }]);
- };
- const delInput = (id: number) => {
- if (inputs.length <= 1) {
- message.warning("至少保留 1 个预设问题");
- return;
- }
- setInputs(inputs.filter(input => input.id !== id));
- };
- const handleChange = (id: number, value: string) => {
- setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
- form.setFieldValue('questionList', inputs.map(input => input.value));
- };
- const handleNext = () => {
- form.validateFields(['name', 'desc', 'appProId', 'iconType']).then((values) => {
- form.setFieldValue('questionList', inputs.map(input => input.value));
- onNext();
- }).catch((error) => {
- console.error(error);
- });
- };
- return (
- <div className='create-step1'>
- <Form.Item
- label='请选择应用图标'
- tooltip='用于在应用广场展示'
- name='iconType'
- rules={[{ required: true, message: '请选择图标' }]}
- >
- <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
- <div style={{ width: 84, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
- <div style={{
- width: 64,
- height: 64,
- borderRadius: 8,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- background: previewBg,
- border: '1px solid #e8e8e8'
- }}>
- {selectedIcon ? (() => {
- const C = (AllIcons as any)[selectedIcon];
- const iconColor = getContrastColor(previewBg);
- return C ? <C style={{ fontSize: 28, color: iconColor }} /> : <span style={{ fontSize: 12 }}>{selectedIcon}</span>;
- })() : <span style={{ color: '#999', fontSize: 12 }}>预览</span>}
- </div>
- </div>
- <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
- <a onClick={() => setIconPickerVisible(true)} style={{ fontSize: 13, color: '#1677ff', cursor: 'pointer' }}>
- 选择图标
- </a>
- <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
- <div style={{ fontSize: 12, color: '#666' }}>背景色:</div>
- <ColorPicker
- presets={presetItems}
- value={previewBg}
- onChange={(color) => {
- const hex = color.toHexString?.() || color?.toString?.() || previewBg;
- setPreviewBg(hex);
- form.setFieldValue('iconColor', hex);
- }}
- />
- </div>
- </div>
- </div>
- </Form.Item>
- <Form.Item
- label='问答应用名称'
- tooltip='尽量概括应用的主要功能'
- name='name'
- rules={[{ required: true, message: '问答应用名称不能为空' }]}
- >
- <Input placeholder="请输入问答应用名称" className='form-input' />
- </Form.Item>
- <Form.Item
- label='应用类型'
- tooltip='应用的实际分类'
- name='typeId'
- >
- <Select
- className='form-input'
- placeholder='请选择问答应用类型'
- onChange={onAppChange}
- allowClear
- >
- {appTypeList.map((item) => (
- <Select.Option key={item.value} value={item.value}>
- {item.label}
- </Select.Option>
- ))}
- </Select>
- </Form.Item>
- {isAppPro && (
- <Form.Item
- label='项目'
- tooltip='应用所属项目'
- name='appProId'
- rules={[{ required: true, message: '项目不能为空' }]}
- >
- <Cascader
- options={appProjectList}
- placeholder="请选择项目"
- showSearch
- className='form-input'
- />
- </Form.Item>
- )}
- <Form.Item
- label='是否公开'
- tooltip='公开应用后,所有用户均可使用该应用,私有应用仅限自己和指定用户使用'
- name='visible'
- >
- <Select
- className='form-input'
- placeholder='请选择是否公开'
- allowClear
- onChange={(value) => {
- onVisibleChange(value);
- }}
- >
- {appVisibleList.map((item) => (
- <Select.Option key={item.value} value={item.value}>
- {item.label}
- </Select.Option>
- ))}
- </Select>
- </Form.Item>
- {userInfo?.tenantId === '000000' && (visibleFlag === '0' || visibleFlag === 0) && (
- <Form.Item
- label='集团公开'
- tooltip='集团下所有用户均可使用该应用'
- name='groupVisible'
- layout='horizontal'
- valuePropName='checked'
- >
- <Select />
- </Form.Item>
- )}
- <Form.Item
- label='显示顺序'
- name='sort'
- tooltip='用于应用广场的显示顺序'
- >
- <InputNumber placeholder="请输入显示顺序" className='form-input' style={{ height: '36px' }} />
- </Form.Item>
- {(visibleFlag === '1' || visibleFlag === 1) && (
- <Form.Item
- label='指定用户'
- tooltip='私有应用的指定用户'
- >
- <div className='tags-info'>
- <div className='tags-list'>
- {vipList.map((item: any) => (
- <Tag
- key={item.userId}
- color="blue"
- closable
- onClose={(e) => {
- e?.preventDefault();
- onRemoveVip(item.userId);
- }}
- >
- {item.userName}
- </Tag>
- ))}
- </div>
- <Space>
- {vipList.length > 0 && (
- <CloseCircleOutlined
- className='cup'
- onClick={() => onRemoveVip('all')}
- />
- )}
- <Button type="primary" variant="outlined" onClick={() => setVipSelectorVisible(true)}>
- 选择
- </Button>
- </Space>
- </div>
- </Form.Item>
- )}
- {/* IconPicker 弹窗 */}
- <IconPicker
- open={iconPickerVisible}
- onClose={() => setIconPickerVisible(false)}
- onSelect={(iconName) => {
- setSelectedIcon(iconName);
- form.setFieldValue('iconType', iconName);
- }}
- value={selectedIcon}
- />
- {/* VIP 用户选择弹窗 */}
- <VipSelector
- open={vipSelectorVisible}
- onClose={() => setVipSelectorVisible(false)}
- onConfirm={(users) => {
- props.onVipConfirm(users);
- setVipSelectorVisible(false);
- }}
- existingUsers={vipList}
- />
- <Form.Item
- label='问答应用描述'
- tooltip='对当前应用功能的描述使用户更了解应用的使用范围'
- name='desc'
- rules={[{ required: true, message: '问答应用描述不能为空' }]}
- >
- <Input.TextArea
- showCount
- maxLength={500}
- placeholder="请输入当前应用的描述"
- className='form-textarea'
- />
- </Form.Item>
- <div className='preset-questions'>
- <h4>添加引导问题</h4>
- <div>
- {inputs.map(input => (
- <div key={input.id} className='question-item'>
- <label>引导问题 {input.id}</label>
- <Input
- className='question-input'
- type="text"
- value={input.value}
- onChange={e => handleChange(input.id, e.target.value)}
- />
- <div className='question-actions'>
- <PlusCircleOutlined className='question-icon' onClick={addInput} />
- <MinusCircleOutlined className='question-icon' onClick={() => delInput(input.id)} />
- </div>
- </div>
- ))}
- </div>
- </div>
- <div className='step-actions'>
- <Button onClick={onBack}>
- 返回
- </Button>
- <Button type='primary' onClick={handleNext}>
- 下一步
- </Button>
- </div>
- </div>
- );
- };
- export default Step1Basic;
|