فهرست منبع

大模型管理提交

yjwang 2 ساعت پیش
والد
کامیت
9316f35496

+ 8 - 0
src/api/company/aiCall.js

@@ -0,0 +1,8 @@
+import request from '@/utils/request'
+
+export function all() {
+    return request({
+        url: '/aicall/kbcat/all',
+        method: 'get'
+    })
+}

+ 42 - 0
src/api/company/aiModel.js

@@ -0,0 +1,42 @@
+import request from '@/utils/request'
+
+export function list(data) {
+    return request({
+        url: '/aicall/account/list',
+        method: 'post',
+        data: data
+    })
+}
+
+
+export function add(data) {
+    return request({
+        url: '/aicall/account/add',
+        method: 'post',
+        data: data
+    })
+}
+
+export function update(data) {
+    return request({
+        url: '/aicall/account/edit',
+        method: 'post',
+        data: data
+    })
+}
+
+
+export function remove(ids) {
+    return request({
+        url: '/aicall/account/remove',
+        method: 'post',
+        params: { ids: ids }
+    })
+}
+
+export function copy(id) {
+    return request({
+        url: `/aicall/account/copy/${id}`,
+        method: 'get'
+    })
+}

+ 8 - 0
src/api/company/aiProvider.js

@@ -0,0 +1,8 @@
+import request from '@/utils/request'
+
+export function all() {
+    return request({
+        url: '/aicall/provider/all',
+        method: 'get'
+    })
+}

+ 564 - 0
src/views/company/aiModel/account/info.vue

@@ -0,0 +1,564 @@
+<template>
+    <div class="app-container">
+        <el-form
+            ref="form"
+            :model="form"
+            :rules="rules"
+            label-width="120px"
+            class="account-form"
+        >
+            <el-form-item label="模型名称" prop="name" required>
+                <el-input v-model="form.name" placeholder="请输入账模型名称" />
+            </el-form-item>
+
+            <el-form-item label="实现类" prop="providerClassName" required>
+                <el-select
+                    v-model="form.providerClassName"
+                    placeholder="请选择实现类"
+                    @change="handleProviderChange"
+                    style="width: 100%"
+                    filterable
+                    clearable
+                >
+                    <el-option
+                        v-for="item in providerOptions"
+                        :key="item"
+                        :label="item"
+                        :value="item"
+                    />
+                </el-select>
+            </el-form-item>
+
+            <el-form-item label="模型并发数" prop="concurrentNum" required>
+                <el-input
+                    v-model="form.concurrentNum"
+                    placeholder="请输入并发数"
+                    @input="handleConcurrentNumInput"
+                />
+            </el-form-item>
+
+            <!-- 动态字段区域 -->
+            <template v-if="dynamicFields.length > 0">
+                <div v-for="field in dynamicFields" :key="field.name">
+                    <el-form-item
+                        :label="field.label"
+                        :prop="'accountJson.' + field.name"
+                        :required="field.required"
+                    >
+                        <!-- 文本框 -->
+                        <el-input
+                            v-if="field.type === 'input'"
+                            v-model="form.accountJson[field.name]"
+                            :placeholder="'请输入' + field.label"
+                        />
+
+                        <!-- 文本域 -->
+                        <el-input
+                            v-else-if="field.type === 'textarea'"
+                            v-model="form.accountJson[field.name]"
+                            type="textarea"
+                            :rows="field.rows || 3"
+                            :placeholder="'请输入' + field.label"
+                        />
+
+                        <!-- 下拉框 -->
+                        <el-select
+                            v-else-if="field.type === 'select'"
+                            v-model="form.accountJson[field.name]"
+                            :placeholder="'请选择' + field.label"
+                            style="width: 100%"
+                            @change="(val) => handleSelectChange(field.name, val)"
+                            filterable
+                            clearable
+                        >
+                            <el-option
+                                v-for="option in field.options"
+                                :key="option.value"
+                                :label="option.label"
+                                :value="option.value"
+                            />
+                        </el-select>
+
+                        <!-- 大文本域(30行) -->
+                        <el-input
+                            v-else-if="field.type === 'large-textarea'"
+                            v-model="form.accountJson[field.name]"
+                            type="textarea"
+                            :rows="30"
+                            :placeholder="'请输入' + field.label"
+                        />
+                    </el-form-item>
+
+                    <!-- Coze的token类型特殊字段 -->
+                    <template v-if="field.name === 'tokenType' && form.accountJson.tokenType">
+                        <el-form-item
+                            v-if="form.accountJson.tokenType === 'oauth'"
+                            label="OAuth配置"
+                            prop="accountJson.oauthFields"
+                        >
+                            <el-card class="oauth-card">
+                                <el-form-item label="Client ID" prop="accountJson.oauthClientId" required>
+                                    <el-input v-model="form.accountJson.oauthClientId" />
+                                </el-form-item>
+                                <el-form-item label="Private Key" prop="accountJson.oauthPrivateKey" required>
+                                    <el-input v-model="form.accountJson.oauthPrivateKey" />
+                                </el-form-item>
+                                <el-form-item label="Public Key ID" prop="accountJson.oauthPublicKeyId" required>
+                                    <el-input v-model="form.accountJson.oauthPublicKeyId" />
+                                </el-form-item>
+                            </el-card>
+                        </el-form-item>
+
+                        <el-form-item
+                            v-if="form.accountJson.tokenType === 'pat'"
+                            label="PAT Token"
+                            prop="accountJson.patToken"
+                            required
+                        >
+                            <el-input
+                                v-model="form.accountJson.patToken"
+                                type="textarea"
+                                :rows="3"
+                                placeholder="请输入PAT Token"
+                            />
+                        </el-form-item>
+                    </template>
+                </div>
+            </template>
+
+            <!-- 中断标志 -->
+            <el-form-item label="打断开关" prop="interruptFlag" required>
+                <el-select v-model="form.interruptFlag" placeholder="请选择中断标志" style="width: 100%"  filterable clearable>
+                    <el-option label="不打断" :value="0" />
+                    <el-option label="关键词打断" :value="1" />
+                    <el-option label="有声音就打断" :value="2" />
+                </el-select>
+            </el-form-item>
+
+            <!-- 中断关键词 -->
+            <el-form-item
+                v-if="form.interruptFlag === 1"
+                label="打断关键词"
+                prop="interruptKeywords"
+            >
+                <el-input
+                    v-model="form.interruptKeywords"
+                    type="textarea"
+                    :rows="5"
+                    placeholder="请输入打断关键词"
+                />
+            </el-form-item>
+
+            <!-- 忽略中断关键词 -->
+            <el-form-item
+                v-if="form.interruptFlag === 1"
+                label="忽略打断关键词"
+                prop="interruptIgnoreKeywords"
+            >
+                <el-input
+                    v-model="form.interruptIgnoreKeywords"
+                    type="textarea"
+                    :rows="5"
+                    placeholder="请输入忽略打断关键词"
+                />
+            </el-form-item>
+
+            <!-- 转人工数字按键 -->
+            <el-form-item label="转人工数字按键" prop="transferManualDigit">
+                <el-input
+                    v-model="form.transferManualDigit"
+                    placeholder="例如: 1"
+                    maxlength="1"
+                    @input="handleTransferManualDigitInput"
+                />
+            </el-form-item>
+
+            <!-- 客户意向提示词 -->
+            <el-form-item
+                v-if="showIntentionTips"
+                label="客户意向提示词"
+                prop="intentionTips"
+            >
+                <el-input
+                    v-model="form.intentionTips"
+                    type="textarea"
+                    :rows="5"
+                    placeholder="请输入客户意向提示词"
+                />
+            </el-form-item>
+        </el-form>
+
+        <!-- 底部按钮 -->
+        <div class="dialog-footer">
+            <el-button @click="handleCancel">取 消</el-button>
+            <el-button type="primary" @click="handleSubmit" :loading="submitting">确 定</el-button>
+        </div>
+    </div>
+</template>
+
+<script>
+import { add, update } from '@/api/company/aiModel'  // 假设有 update 接口
+import { all } from '@/api/company/aiCall'
+
+export default {
+    name: 'AccountForm',
+    props: {
+        providerOptions: { type: Array, default: () => [] },
+        initialData: {
+            type: Object,
+            default: () => ({})
+        },
+        errorMsg: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            submitting: false,
+            // 表单数据
+            form: {
+                id: undefined,
+                name: '',
+                providerClassName: '',
+                concurrentNum: '',
+                interruptFlag: 0,
+                interruptKeywords: '',
+                interruptIgnoreKeywords: '呃 哦 哦哦 嗯 嗯嗯 嗯好的 好的 对 对对 是的 明白 啊 这样啊 是这样啊这样的 您好 你好',
+                transferManualDigit: '',
+                intentionTips: '',
+                accountJson: {}
+            },
+            // 知识库分类选项
+            kbCatOptions: [],
+            // 动态字段配置
+            dynamicFields: [],
+            // 是否显示客户意向提示词
+            showIntentionTips: false
+        }
+    },
+    computed: {
+        // 表单验证规则
+        rules() {
+            return {
+                name: [
+                    { required: true, message: '请输入账户名称', trigger: 'blur' }
+                ],
+                providerClassName: [
+                    { required: true, message: '请选择实现类', trigger: 'change' }
+                ],
+                concurrentNum: [
+                    { required: true, message: '请输入并发数', trigger: 'blur' },
+                    { pattern: /^\d+$/, message: '请输入数字', trigger: 'blur' },
+                    { validator: this.validateConcurrentNum, trigger: 'blur' }
+                ],
+                transferManualDigit: [
+                    { pattern: /^[0-9]?$/, message: '请输入单个数字0-9', trigger: 'blur' }
+                ]
+            }
+        }
+    },
+    watch: {
+        // 监听初始数据变化
+        initialData: {
+            handler(val) {
+                if (val && Object.keys(val).length > 0) {
+                    this.initFormData(val)
+                }
+            },
+            deep: true,
+            immediate: true
+        },
+        // 监听错误信息
+        errorMsg: {
+            handler(val) {
+                if (val) {
+                    this.$alert(val, '错误', {
+                        type: 'error',
+                        confirmButtonText: '确定'
+                    })
+                }
+            },
+            immediate: true
+        }
+    },
+    created() {
+        this.loadKbCatOptions()
+    },
+    methods: {
+        // 初始化表单数据
+        initFormData(data) {
+            this.form.id = data.id
+            this.form.name = data.name || ''
+            this.form.providerClassName = data.providerClassName || ''
+            this.form.concurrentNum = data.concurrentNum || ''
+            this.form.interruptFlag = data.interruptFlag || 0
+            this.form.interruptKeywords = data.interruptKeywords || ''
+            this.form.interruptIgnoreKeywords = data.interruptIgnoreKeywords || ''
+            this.form.transferManualDigit = data.transferManualDigit || ''
+            this.form.intentionTips = data.intentionTips || ''
+
+            // 解析accountJson
+            try {
+                this.form.accountJson = data.accountJson ?
+                    (typeof data.accountJson === 'string' ? JSON.parse(data.accountJson) : data.accountJson)
+                    : {}
+            } catch (e) {
+                console.error('解析accountJson失败:', e)
+                this.form.accountJson = {}
+            }
+
+            // 根据providerClassName更新动态字段
+            if (this.form.providerClassName) {
+                this.updateDynamicFields(this.form.providerClassName)
+            }
+        },
+
+        // 并发数输入处理
+        handleConcurrentNumInput(value) {
+            // 只保留数字
+            let val = value.replace(/[^0-9]/g, '')
+            // 去掉前导0
+            val = val.replace(/^0+(\d)/, '$1')
+            // 上限200
+            if (parseInt(val) > 200) val = '200'
+            this.$set(this.form, 'concurrentNum', val)
+        },
+
+        // 转人工数字按键输入处理
+        handleTransferManualDigitInput(value) {
+            // 只保留0-9的数字,并限制为单个数字
+            let val = value.replace(/[^0-9]/g, '')
+            if (val.length > 1) val = val.substring(0, 1)
+            this.$set(this.form, 'transferManualDigit', val)
+        },
+
+        // 并发数验证器
+        validateConcurrentNum(rule, value, callback) {
+            const num = parseInt(value)
+            if (isNaN(num) || num < 0 || num > 200) {
+                callback(new Error('请输入0-200之间的整数'))
+            } else {
+                callback()
+            }
+        },
+
+        // 实现类变更处理
+        handleProviderChange(val) {
+            this.updateDynamicFields(val)
+        },
+
+        // 更新动态字段
+        updateDynamicFields(providerClassName) {
+            this.dynamicFields = []
+            this.showIntentionTips = false
+
+            // 清空accountJson中不相关的字段
+            const newAccountJson = {}
+
+            if (['DeepSeekChat', 'ChatGpt4o', 'JiutianChat'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
+                    { name: 'modelName', label: '模型名称', type: 'input', required: true },
+                    { name: 'llmTips', label: '大模型提示词', type: 'large-textarea', required: true },
+                    { name: 'faqContext', label: 'FAQ上下文', type: 'large-textarea', required: true },
+                    { name: 'kbCatId', label: '知识库分类', type: 'select', required: false, options: this.kbCatOptions },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
+                this.showIntentionTips = true
+            }
+
+            if (['LocalLlmChat'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'modelName', label: '模型名称', type: 'input', required: true },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
+            }
+
+            if (['LocalNlpChat'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'botId', label: 'botId', type: 'input', required: true },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true }
+                ]
+            }
+
+            if (['Coze'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'botId', label: 'botId', type: 'input', required: true },
+                    {
+                        name: 'tokenType',
+                        label: 'Token类型',
+                        type: 'select',
+                        required: true,
+                        options: [
+                            { label: 'OAuth', value: 'oauth' },
+                            { label: 'PAT', value: 'pat' }
+                        ]
+                    },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
+            }
+
+            if (['MaxKB', 'Dify'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
+            }
+
+            if (['JiutianWorkflow', 'JiutianAgent'].includes(providerClassName)) {
+                this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
+                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
+                    { name: 'botId', label: 'botId', type: 'input', required: true },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
+            }
+
+            // 保留原有数据
+            this.dynamicFields.forEach(field => {
+                if (this.form.accountJson[field.name]) {
+                    newAccountJson[field.name] = this.form.accountJson[field.name]
+                }
+            })
+
+            // 合并数据
+            this.form.accountJson = {
+                ...newAccountJson,
+                ...this.form.accountJson
+            }
+        },
+
+        // 下拉框变更处理
+        handleSelectChange(fieldName, value) {
+            if (fieldName === 'tokenType') {
+                // 清空相关字段
+                delete this.form.accountJson.oauthClientId
+                delete this.form.accountJson.oauthPrivateKey
+                delete this.form.accountJson.oauthPublicKeyId
+                delete this.form.accountJson.patToken
+            }
+        },
+
+        // 加载知识库分类选项
+        async loadKbCatOptions() {
+            try {
+                const response = await all();
+                const list = response?.data ?? [];
+                this.kbCatOptions = Array.isArray(list)
+                    ? list.map(item => ({ label: item.cat, value: item.id }))
+                    : [];
+            } catch (error) {
+                console.error('加载知识库分类失败:', error);
+                this.kbCatOptions = [];
+            }
+        },
+
+        // 取消
+        handleCancel() {
+            this.$emit('cancel')
+        },
+
+        // 提交
+        async handleSubmit() {
+            await this.$refs.form.validate(async (valid) => {
+                if (valid) {
+                    this.submitting = true
+
+                    try {
+                        if (!this.showIntentionTips) {
+                            this.form.intentionTips = ''
+                        }
+
+                        const submitData = {
+                            id: this.form.id,
+                            name: this.form.name,
+                            providerClassName: this.form.providerClassName,
+                            concurrentNum: this.form.concurrentNum,
+                            interruptFlag: this.form.interruptFlag,
+                            interruptKeywords: this.form.interruptKeywords || '',
+                            interruptIgnoreKeywords: this.form.interruptIgnoreKeywords || '',
+                            transferManualDigit: this.form.transferManualDigit || '',
+                            intentionTips: this.form.intentionTips || ''
+                        }
+
+                        if (this.form.accountJson) {
+                            Object.keys(this.form.accountJson).forEach(key => {
+                                submitData[key] = this.form.accountJson[key]
+                            })
+                        }
+
+                        submitData.accountJson = JSON.stringify(this.form.accountJson)
+
+                        let response;
+                        if (submitData.id) {
+                            // 修改操作
+                            response = await update(submitData);
+                        } else {
+                            // 新增操作
+                            delete submitData.id;
+                            response = await add(submitData);
+                        }
+
+                        this.$message.success('操作成功!')
+                        this.$emit('success', response)
+                    } catch (error) {
+                        console.error('提交失败:', error)
+                        this.$message.error('操作失败')
+                    } finally {
+                        this.submitting = false
+                    }
+                }
+            })
+        }
+    }
+}
+</script>
+
+<style scoped>
+.app-container {
+    padding: 20px;
+}
+
+.account-form {
+    max-width: 800px;
+    margin: 0 auto;
+}
+
+.dialog-footer {
+    text-align: center;
+    padding-top: 20px;
+    border-top: 1px solid #eee;
+    margin-top: 20px;
+}
+
+.oauth-card {
+    margin-bottom: 20px;
+}
+
+.oauth-card :deep(.el-card__body) {
+    padding: 20px;
+}
+</style>

+ 379 - 0
src/views/company/aiModel/index.vue

@@ -0,0 +1,379 @@
+<template>
+    <div class="app-container">
+        <!-- 搜索表单 -->
+        <el-form
+            v-show="showSearch"
+            ref="queryForm"
+            :inline="true"
+            :model="queryParams"
+            label-width="100px"
+        >
+            <el-form-item label="模型名称" prop="name">
+                <el-input
+                    v-model="queryParams.name"
+                    clearable
+                    placeholder="请输入"
+                    size="small"
+                    style="width: 200px"
+                    @keyup.enter.native="handleQuery"
+                />
+            </el-form-item>
+            <el-form-item label="实现类" prop="providerClassName">
+                <el-select
+                    v-model="queryParams.providerClassName"
+                    clearable
+                    placeholder="全部"
+                    size="small"
+                    style="width: 200px"
+                >
+                    <el-option label="全部" value="" />
+                    <el-option
+                        v-for="item in providerClassNameList"
+                        :key="item.id"
+                        :label="item.providerClassName"
+                        :value="item.providerClassName"
+                    />
+                </el-select>
+            </el-form-item>
+            <el-form-item>
+                <el-button icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+                <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+            </el-form-item>
+        </el-form>
+
+        <!-- 工具栏 -->
+        <el-row :gutter="10" class="mb8">
+            <el-col :span="1.5">
+                <el-button
+                    icon="el-icon-plus"
+                    plain
+                    size="mini"
+                    type="primary"
+                    @click="handleAdd"
+                >新增</el-button>
+            </el-col>
+            <el-col :span="1.5">
+                <el-button
+                    icon="el-icon-edit"
+                    plain
+                    size="mini"
+                    type="success"
+                    @click="handleEdit()"
+                >修改</el-button>
+            </el-col>
+            <el-col :span="1.5">
+                <el-button
+                    icon="el-icon-delete"
+                    plain
+                    size="mini"
+                    type="danger"
+                    @click="handleDelete()"
+                >删除</el-button>
+            </el-col>
+            <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
+        </el-row>
+
+        <!-- 数据表格 -->
+        <el-table v-loading="loading" :data="modelList" border @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column align="center" label="模型名称" prop="name" width="400" />
+            <el-table-column align="center" label="实现类" prop="providerClassName" width="400" />
+            <el-table-column
+                align="center"
+                class-name="small-padding fixed-width"
+                label="操作"
+                width="200"
+            >
+                <template slot-scope="scope">
+                    <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-document-copy"
+                        @click="handleCopy(scope.row)"
+                    >复制</el-button>
+                    <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-edit"
+                        @click="handleEdit(scope.row)"
+                    >编辑</el-button>
+                    <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-delete"
+                        @click="handleDelete(scope.row)"
+                    >删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <pagination
+            v-show="total > 0"
+            :limit.sync="queryParams.pageSize"
+            :page.sync="queryParams.pageNum"
+            :total="total"
+            @pagination="getList"
+        />
+
+        <!-- 新增/编辑弹窗 -->
+        <el-dialog
+            :title="dialogTitle"
+            :visible.sync="dialogVisible"
+            width="700px"
+            append-to-body
+            @close="handleDialogClose"
+            :fullscreen="isFullscreen"
+        >
+            <template slot="title">
+                <span>{{ dialogTitle }}</span>
+                <div style="display: inline-block; margin-left: 10px;">
+                    <el-tooltip :content="isFullscreen ? '退出全屏' : '全屏'" placement="top">
+                        <el-button
+                            type="text"
+                            icon="el-icon-full-screen"
+                            @click="toggleFullscreen"
+                            style="font-size: 16px;"
+                        />
+                    </el-tooltip>
+                </div>
+            </template>
+
+            <account-form
+                v-if="dialogVisible"
+                ref="accountForm"
+                :initial-data="currentRow"
+                :provider-options="providerOptions"
+                @cancel="handleDialogClose"
+                @success="handleFormSuccess"
+            />
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+import { list, remove } from '@/api/company/aiModel'
+import { all } from '@/api/company/aiProvider'
+import Info from '@/views/company/aiModel/account/info.vue'
+
+export default {
+    name: 'AiModelList',
+    components: {
+        AccountForm: Info
+    },
+    data() {
+        return {
+            selectedRows: [],
+            isFullscreen: false,
+            // 遮罩层
+            loading: false,
+            // 显示搜索条件
+            showSearch: true,
+            // 总条数
+            total: 0,
+            // 表格数据
+            modelList: [],
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                name: undefined,
+                providerClassName: undefined
+            },
+            // 实现类列表(用于搜索下拉和弹窗下拉)
+            providerClassNameList: [],
+            // 弹窗控制
+            dialogVisible: false,
+            dialogTitle: '新增模型账户',
+            currentRow: {},
+        }
+    },
+    computed: {
+        providerOptions() {
+            return this.providerClassNameList.map(item => item.providerClassName)
+        }
+    },
+    created() {
+        this.getList()
+        this.getAll()
+    },
+    methods: {
+        toggleFullscreen() {
+            this.isFullscreen = !this.isFullscreen;
+        },
+        /** 查询列表 */
+        getList() {
+            this.loading = true
+            list(this.queryParams)
+                .then((response) => {
+                    this.modelList = response.rows || []
+                    this.total = response.total || 0
+                    this.loading = false
+                })
+                .catch(() => {
+                    this.loading = false
+                })
+        },
+        /** 搜索按钮操作 */
+        handleQuery() {
+            this.queryParams.pageNum = 1
+            this.getList()
+        },
+        /** 重置按钮操作 */
+        resetQuery() {
+            this.resetForm('queryForm')
+            this.handleQuery()
+        },
+        /** 获取所有实现类 */
+        getAll() {
+            all().then((response) => {
+                this.providerClassNameList = response.data || []
+            })
+        },
+        /** 新增按钮 */
+        handleAdd() {
+            this.dialogTitle = '新增模型账户'
+            this.currentRow = {} // 清空当前行
+            this.dialogVisible = true
+            this.isFullscreen = false;
+        },
+        /** 弹窗关闭时清理 */
+        handleDialogClose() {
+            this.isFullscreen = false;
+            this.dialogVisible = false
+            this.currentRow = {};
+        },
+        /** 表单提交成功回调(由子组件触发) */
+        handleFormSuccess(response) {
+            this.dialogVisible = false
+            this.getList()
+        },
+        /** 修改按钮操作 */
+        handleEdit(row) {
+            let editRow = row;
+            // 如果没有传入 row(即点击工具栏按钮),则从选中行获取
+            if (!editRow) {
+                if (this.selectedRows.length !== 1) {
+                    this.$message.warning('请选择一条要修改的数据');
+                    return;
+                }
+                editRow = this.selectedRows[0];
+            }
+
+            this.dialogTitle = '编辑模型账户';
+            this.currentRow = { ...editRow };
+            this.dialogVisible = true;
+            this.isFullscreen = false;
+        },
+        /** 表格多选选中事件 */
+        handleSelectionChange(selection) {
+            this.selectedRows = selection;
+        },
+        /** 删除按钮操作 */
+        handleDelete(row) {
+            let ids = [];
+            if (row) {
+                // 单行删除
+                ids = [row.id];
+            } else {
+                // 批量删除(从选中行获取)
+                if (this.selectedRows.length === 0) {
+                    this.$message.warning('请至少选择一条要删除的数据');
+                    return;
+                }
+                ids = this.selectedRows.map(item => item.id);
+            }
+
+            // 确认对话框
+            this.$confirm(`此操作将永久删除${ids.length > 1 ? '这些' : '该'}记录,是否继续?`, '警告', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                this.handleDeleteConfirm(ids);
+            }).catch(() => {});
+        },
+        /** 执行删除操作 */
+        async handleDeleteConfirm(ids) {
+            try {
+                const response = await remove(ids.join(','));
+                if (response.code === 200) {
+                    this.$message.success('删除成功');
+                    this.getList(); // 刷新列表
+                } else {
+                    this.$message.error(response.msg || '删除失败');
+                }
+            } catch (error) {
+                console.error('删除失败:', error);
+                this.$message.error('删除失败');
+            }
+        },
+        /** 复制按钮操作 */
+        handleCopy(row) {
+            // 前端直接复制数据
+            const copiedRow = { ...row };
+            // 清空ID,确保是新增操作
+            delete copiedRow.id;
+            // 添加"-副本"后缀
+            copiedRow.name = copiedRow.name + '-副本';
+            
+            this.dialogTitle = '复制模型账户';
+            this.currentRow = copiedRow;
+            this.dialogVisible = true;
+            this.isFullscreen = false;
+        }
+    }
+}
+</script>
+
+<style scoped>
+/* 原有样式保持不变 */
+.record-container {
+    padding: 20px;
+}
+
+.record-info {
+    margin-bottom: 20px;
+    padding: 10px;
+    background: #f5f7fa;
+    border-radius: 4px;
+}
+
+.record-info p {
+    margin: 5px 0;
+}
+
+.record-controls {
+    text-align: center;
+}
+
+.record-status {
+    margin-bottom: 20px;
+    font-size: 14px;
+    color: #606266;
+}
+
+.recording-indicator {
+    color: #f56c6c;
+}
+
+.record-buttons {
+    margin-bottom: 20px;
+}
+
+.record-buttons .el-button {
+    width: 60px;
+    height: 60px;
+    font-size: 24px;
+}
+
+.record-preview {
+    margin-top: 20px;
+}
+
+.el-dialog__wrapper /deep/ .el-dialog--fullscreen .el-dialog__body {
+    height: calc(100vh - 120px);
+    overflow-y: auto;
+    padding: 20px;
+}
+</style>