Kaynağa Gözat

合并渠道号

朱鑫波 3 yıl önce
ebeveyn
işleme
15e5f97299

+ 1 - 1
src/utils/request.js

@@ -142,4 +142,4 @@ const installer = {
 export {
 	installer as VueAxios,
 	service as axios
-};
+};

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1102 - 0
src/views/modules/autoLaunch/channelManage/channelDetailList.vue


+ 17 - 1
src/views/modules/autoLaunch/channelManage/channelManage.less

@@ -1,4 +1,3 @@
-
 .channelManage {
     .middle-button {
         margin: 0 8px;
@@ -41,3 +40,20 @@
         padding: 20px;
     }
 }
+.channel-modal-content {
+    width: 100%;
+    height: 300px;
+    display: flex;
+    flex-direction: column;
+    .channel-modal-content-button {
+        width: 100%;
+        height: 100%;
+        margin: 10px;
+        display: flex;
+        align-items: center;
+        .introduce{
+            margin-left: 10px;
+        }
+
+    }
+}

+ 514 - 0
src/views/modules/autoLaunch/channelManage/components/creatApplication.vue

@@ -0,0 +1,514 @@
+<style lang="less" scoped>
+.require-class {
+    /deep/ .ant-form-item-label {
+        label::before {
+            display: inline-block;
+            margin-right: 4px;
+            color: #f5222d;
+            font-size: 14px;
+            font-family: SimSun, sans-serif;
+            line-height: 1;
+            content: '*';
+        }
+    }
+}
+</style>
+<template>
+    <div>
+        <a-modal
+            title="添加应用"
+            :visible="application"
+            @ok="handleSubmit"
+            :confirmLoading="confirmLoading"
+            @cancel="handleCancel"
+            width="800px"
+        >
+            <a-card :body-style="{ padding: '24px 32px' }" :bordered="false">
+                <a-form :form="form">
+                    <a-form-item
+                        label="下载链接"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    >
+                        <a-input
+                            placeholder="请填写应用下载链接"
+                            v-decorator="[
+                                'url',
+                                {
+                                    rules: [{ required: true, validator: handleConfirmValue }]
+                                }
+                            ]"
+                            id="url"
+                        />
+                        <a-tag
+                            color="#2db7f5"
+                            @click="getChangeAll('渠道号', 'form', 'url')"
+                            v-if="createTypeData && createTypeData.createType == '1'"
+                            >+渠道号</a-tag
+                        >
+                    </a-form-item>
+
+                    <a-form-item
+                        label="应用包名"
+                        v-if="uploadType == '2' && appType == 'android'"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    >
+                        <a-input
+                            v-decorator="[
+                                'packageName',
+                                {
+                                    rules: [{ required: true, message: '请填写正确的应用包名称' }]
+                                }
+                            ]"
+                            placeholder="请填写包名称,如com.smile.gifmaker"
+                        />
+                    </a-form-item>
+
+                    <a-form-item
+                        label="应用名称"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    >
+                        <a-input
+                            v-decorator="[
+                                'appName',
+                                {
+                                    rules: [{ required: true, message: '请填写应用名称' }]
+                                }
+                            ]"
+                            placeholder="请填写应用名称"
+                        />
+                    </a-form-item>
+
+                    <a-form-item
+                        label="上传图标"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 19 } }"
+                        v-if="application"
+                        class="require-class"
+                    >
+                        <uploadFile
+                            v-if="application"
+                            :value.sync="fileUrl"
+                            :multiple="false"
+                            :fileCount="1"
+                            uploadType="image"
+                            @overUpload="overUpload"
+                            :size="100"
+                        ></uploadFile>
+                        <p class="upload-txt-rules">文件格式:支持上传png/jpg/jpeg图片,尺寸450*450,小于100kb</p>
+                    </a-form-item>
+                    <a-form-item
+                        label="应用标记"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    >
+                        <a-input
+                            v-decorator="[
+                                'appVersion',
+                                {
+                                    rules: [{ required: true, message: '应用标记必填' }]
+                                }
+                            ]"
+                            placeholder="请填写应用标记,必填且不可重复,如快手-5.6.1.66"
+                            id="appVersion"
+                        />
+                        <a-tag
+                            color="#2db7f5"
+                            @click="getChangeAll('渠道号', 'form', 'appVersion')"
+                            v-if="createTypeData && createTypeData.createType == '1'"
+                            >+渠道号</a-tag
+                        >
+                    </a-form-item>
+                    <a-form-item
+                        label="隐私政策链接"
+                        :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                        :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    >
+                        <a-input
+                            v-decorator="[
+                                'appPrivacyUrl',
+                                {
+                                    rules: [
+                                        { required: true, message: '请填写隐私政策链接' },
+                                        { validator: handleConfirmValue }
+                                    ]
+                                }
+                            ]"
+                            placeholder="请填写隐私政策链接"
+                        />
+                    </a-form-item>
+                </a-form>
+            </a-card>
+        </a-modal>
+    </div>
+</template>
+
+<script>
+import Vue from 'vue'
+import { ACCESS_TOKEN } from '@/store/mutation-types'
+import { deleteAction, postAction, getAction, downFilePost, downFile } from '@/api/manage'
+import uploadFile from './uploadFile.vue'
+import { mapActions, mapGetters } from 'vuex'
+import BMF from 'browser-md5-file'
+import selectTable from '@/views/modules/Statistics/components/selectPagination.vue'
+import $ from 'jquery'
+const fileSuffix = ['xlsx', 'xls', 'zip']
+export default {
+    name: 'BaseForm',
+    components: {
+        uploadFile,
+        selectTable
+    },
+    data() {
+        return {
+            useSdk: [],
+            loadDisabled: false,
+            projectId: '',
+            accountId: '',
+            fileList: [],
+            fileUrl: '',
+            accountList: {},
+            appType: 'android', //设备类型
+            platform: '1', //应用类型   : 1 Android 应用下载,2: Android 网页游戏,3: iOS 应用下载, 4:iOS 网页游戏
+            uploadType: '2', //上传类型 1 本地上传 2 填写链接
+            appVersion: '', //应用标记
+            appName: '', // 应用名称
+            packageName: '', //应用包名
+            file: '', // 上传文件
+            imageUrl: '', // 上传图标链接链接
+            downloadUrl: '', //下载链接
+            showUploadFile: true,
+            showDownLoad: false,
+            showUpload: true,
+            showAndroid: true,
+            showIOS: false,
+            customDomain: '',
+            acceptVideo: '/*.apk,/*.png,/*.jpg,/*.jpeg,/*.mp4',
+            accessKeyId: 'LTAIbNbqWzSOklQV',
+            accessKeySecret: '1rkPz7JNoXk8sJevPaeYHWqfkQXBGh',
+            region: 'oss-cn-beijing',
+            bucket: 'ctop-media',
+            form: this.$form.createForm(this),
+            url: {
+                insertTemplateUrl: '/kuaishouAppPackage/fillInLink',
+                userAllocationList: '/ctop/userAllocation/list'
+            },
+            application: false,
+            confirmLoading: false,
+            imageToken: '',
+            // uploadAction: '/kuaishouAppPackage/importAppExcel',
+            tokenHeader: { 'X-Access-Token': Vue.ls.get(ACCESS_TOKEN) },
+            okCreated: false,
+            dataFile: null,
+            createTypeData: null
+        }
+    },
+    methods: {
+        getChangeAll(type, form, field) {
+            var node = document.getElementById(field)
+            var v = '-{{' + type + '}}-'
+            if (document.selection) {
+                //IE
+                node.focus()
+                var sel = document.selection.createRange()
+                sel.text = v
+                sel.select()
+                if (field == 'channelName') {
+                    this[form].setFieldsValue({ channelName: sel.text })
+                } else if (field == 'channelCode') {
+                    this[form].setFieldsValue({ channelCode: sel.text })
+                } else if (field == 'url') {
+                    this[form].setFieldsValue({ url: sel.text })
+                } else if (field == 'clickTrackUrl') {
+                    this[form].setFieldsValue({ clickTrackUrl: sel.text })
+                } else if (field == 'actionbarClickUrl') {
+                    this[form].setFieldsValue({ actionbarClickUrl: sel.text })
+                } else if (field == 'appVersion') {
+                    this[form].setFieldsValue({ appVersion: sel.text })
+                }
+            } else if (node.selectionStart || node.selectionStart == '0') {
+                //MOZILLA/GOOGLE
+                var startPos = node.selectionStart
+                var endPos = node.selectionEnd
+                var restoreTop = node.scrollTop
+                node.value = node.value.substring(0, startPos) + v + node.value.substring(endPos, node.value.length)
+                if (restoreTop > 0) {
+                    node.scrollTop = restoreTop
+                }
+                if (field == 'channelName') {
+                    this[form].setFieldsValue({ channelName: node.value })
+                } else if (field == 'channelCode') {
+                    this[form].setFieldsValue({ channelCode: node.value })
+                } else if (field == 'url') {
+                    this[form].setFieldsValue({ url: node.value })
+                } else if (field == 'clickTrackUrl') {
+                    this[form].setFieldsValue({ clickTrackUrl: node.value })
+                } else if (field == 'actionbarClickUrl') {
+                    this[form].setFieldsValue({ actionbarClickUrl: node.value })
+                } else if (field == 'appVersion') {
+                    this[form].setFieldsValue({ appVersion: node.value })
+                }
+                node.focus()
+                node.selectionStart = startPos + v.length
+                node.selectionEnd = startPos + v.length
+                // this.channelName = node.value
+            } else {
+                //OTHER
+                node.value += v
+                // console.log(node.value)
+                if (field == 'channelName') {
+                    this[form].setFieldsValue({ channelName: node.value })
+                } else if (field == 'channelCode') {
+                    this[form].setFieldsValue({ channelCode: node.value })
+                } else if (field == 'url') {
+                    this[form].setFieldsValue({ url: node.value })
+                } else if (field == 'clickTrackUrl') {
+                    this[form].setFieldsValue({ clickTrackUrl: node.value })
+                } else if (field == 'actionbarClickUrl') {
+                    this[form].setFieldsValue({ actionbarClickUrl: node.value })
+                } else if (field == 'appVersion') {
+                    this[form].setFieldsValue({ appVersion: node.value })
+                }
+
+                node.focus()
+                // this.channelName = node.value
+            }
+        },
+        overUpload(file) {},
+        downLoadFile() {
+            downFile('/kuaishouAppPackage/exportAppTemplate').then(res => {
+                let blob = new Blob([res], {
+                    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+                })
+                let downloadElement = document.createElement('a')
+                let href = window.URL.createObjectURL(blob) //创建下载的链接
+                downloadElement.href = href
+                downloadElement.download = '应用模板' //下载后文件名
+                document.body.appendChild(downloadElement)
+                downloadElement.click() //点击下载
+                document.body.removeChild(downloadElement) //下载完成移除元素
+                window.URL.revokeObjectURL(href) //释放掉blob对象
+            })
+        },
+        handleChange(info) {
+            this.loadDisabled = false
+            this.$message.success('文件导入成功')
+        },
+        beforeUpload(file) {
+            this.dataFile = $('.fileUpload')
+                .eq(0)
+                .find('input')[0].files[0]
+            this.loadDisabled = true
+            const size = (Number(file.size) / (1024 * 1024)).toFixed(2)
+            const fileName = file.name
+            const fileStrArr = fileName.split('.')
+            const suffix = fileStrArr[fileStrArr.length - 1]
+
+            let result = fileSuffix.some(item => {
+                return item === suffix
+            })
+
+            if (!result) {
+                this.$message.error('不支持该文件类型')
+                this.loadDisabled = false
+                return false
+            }
+        },
+        cosUpload(file) {
+            let that = this
+            let date = new Date()
+            let y = date.getFullYear()
+            let MM = date.getMonth() + 1
+            MM = MM < 10 ? '0' + MM : MM
+            let d = date.getDate()
+            d = d < 10 ? '0' + d : d
+            let timeElse = new Date().getTime()
+            let arr = file.name.split('.')
+            let str = ''
+            for (let i = 0; i < arr.length; i++) {
+                if (i === arr.length - 1) {
+                } else {
+                    if (i === arr.length - 2) {
+                        str += arr[i]
+                    } else {
+                        str += arr[i] + '.'
+                    }
+                }
+            }
+            cos.putObject(
+                {
+                    Bucket: 'media-1301855440',
+                    Region: 'ap-chongqing',
+                    // 存储桶所在地域,必须字段
+                    Key:
+                        that.appType +
+                        '/' +
+                        y +
+                        '-' +
+                        MM +
+                        '-' +
+                        d +
+                        '/' +
+                        str +
+                        '-' +
+                        timeElse +
+                        '.' +
+                        arr[arr.length - 1],
+                    StorageClass: 'STANDARD',
+                    Body: file // 上传文件对象
+                },
+                function(err, data) {
+                    if (err) {
+                        that.$message.error('上传失败!!!' + err)
+                        return
+                    }
+                    that.loadingElse = false
+                    that.fileList.push({
+                        uid: file.name,
+                        name: file.name,
+                        status: 'done',
+                        url: '//' + data.Location
+                    })
+                    if (that.fileList.length > 1) {
+                        that.fileList = that.fileList.slice(-1)
+                    }
+                    const isZip = file.type.includes('zip') || file.type.includes('text')
+                    const fileOvesize = file.size > 1024 * 1024 * 1024
+                    if (!isZip) {
+                        that.$message.error('只能上传zip格式的文件或者txt文件')
+                        return
+                    }
+                    if (fileOvesize) {
+                        that.$message.error('文件大小不能超过1G')
+                        return
+                    }
+                    that.handleMd5(file)
+                }
+            )
+        },
+        handleMd5(file) {
+            let bmf = new BMF()
+            let that = this
+            return new Promise(function(resolve, reject) {
+                bmf.md5(file, (err, md5) => {
+                    // getAction('kuaishouPopulationPackage/checkPopulationPackage', { signature: md5 })
+                    //   .then((result) => {
+                    //     if (result.code === 200) {
+                    that.personNameDisabled = false
+                    that.personResourceDisabled = false
+                    resolve()
+                })
+            })
+        },
+        handleOk() {
+            this.handleSubmit()
+        },
+        handleCancel() {
+            this.dataFile = null
+            this.appType = 'android'
+            this.okCreated = false
+            this.form.resetFields()
+            this.fileUrl = ''
+            this.file = ''
+            this.platform = '1'
+            this.application = false
+        },
+        showApplication() {
+            this.application = true
+        },
+        handleConfirmValue(rule, value, callback) {
+            if (/(http|https):\/\/([\w.]+\/?)\S*/.test(value) && value != '') {
+                callback()
+            } else {
+                callback('链接不能为空且开头为http://或https://')
+            }
+        },
+        ...mapGetters(['nickname', 'avatar', 'userInfo']),
+        changePlatform() {
+            this.form.resetFields()
+            if (this.appType == 'android') {
+                this.showAndroid = true
+                this.showIOS = false
+                this.platform = '1'
+            } else {
+                this.showAndroid = false
+                this.showIOS = true
+                this.showDownLoad = true
+                this.platform = '1'
+            }
+        },
+        changeShowUpload() {
+            this.form.resetFields()
+            this.fileUrl = ''
+        },
+        changeShowUploadType() {
+            this.form.resetFields()
+            if (this.platform == '2') {
+                this.showUpload = false
+                this.showUploadFile = false
+                this.showDownLoad = true
+                this.uploadType = '1'
+            } else {
+                this.showUpload = true
+                this.showUploadFile = true
+                this.showDownLoad = false
+            }
+        },
+
+        handleSubmit() {
+            //   console.log('设备类型:' + this.appType)
+            //   console.log('应用类型:' + this.platform)
+            //   console.log('上传类型:' + this.uploadType)
+            //   console.log('应用标记:' + this.appVersion)
+            //   console.log('应用名称:' + this.appName)
+            //   console.log('包名称:' + this.packageName)
+            //   console.log('文件地址:' + this.file)
+            //   console.log('图片地址:' + this.imageUrl)
+            //   e.preventDefault()
+
+            this.form.validateFields((err, values) => {
+                if (!err) {
+                    if (this.fileUrl) {
+                        this.confirmLoading = true
+                        let params = {}
+
+                        this.url.insertTemplateUrl = '/kuaishouAppPackage/fillInLink'
+                        params = { ...values }
+                        params.appIconUrl = this.fileUrl
+                        this.$emit('getDataList', params)
+                        this.handleCancel()
+                        // postAction(this.url.insertTemplateUrl, params).then(res => {
+                        //     this.confirmLoading = false
+                        //     if (res.success) {
+                        //         this.$message.success('创建成功')
+                        //         this.handleCancel()
+                        //         this.$emit('getDataList')
+                        //     } else {
+                        //         this.$message.error(res.message)
+                        //     }
+                        // })
+                    } else {
+                        this.$message.error('请填写必填项')
+                    }
+                }
+            })
+        },
+
+        onChange(value) {}
+    },
+    created: function() {
+        this.createTypeData = JSON.parse(localStorage.getItem('createTypeData'))
+        let params = {}
+        params.pageSize = '1000'
+        params.mediaId = '2'
+        params.userId = this.userInfo().id
+        getAction(this.url.userAllocationList, params).then(res => {
+            if (res.success) {
+                this.accountList = res.result.records
+            }
+        })
+    }
+}
+</script>

+ 244 - 0
src/views/modules/autoLaunch/channelManage/components/treeSelectAccount.vue

@@ -0,0 +1,244 @@
+<style lang="scss" scoped>
+.select-project {
+    width: 50%;
+}
+</style>
+<template>
+    <div class="select-project">
+        <treeselect
+            :multiple="multiple"
+            :disable-branch-nodes="!multiple"
+            :disable-fuzzy-matching="true"
+            :options="options"
+            :load-options="loadOptions"
+            placeholder="请选择项目以及账户"
+            v-model="selectedVal"
+            :value-consists-of="valueConsistsOf"
+            :auto-load-root-options="false"
+            @select="update"
+            @input="emitValue"
+            :limit="limit"
+            :default-options="true"
+            :disabled="disabled"
+            :limitText="
+                count => {
+                    return '隐藏' + count + '条'
+                }
+            "
+            noChildrenText="该项目下暂无账号"
+            :noOptionsText="showLoading"
+        />
+    </div>
+</template>
+
+<script>
+//   @open="getParticipateList"
+// import the component
+import Treeselect from '@riophae/vue-treeselect'
+// import the styles
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+
+import { getAction, postAction } from '@/api/manage'
+import { mapGetters } from 'vuex'
+// We just use `setTimeout()` here to simulate an async operation
+// instead of requesting a real API server for demo purpose.
+let called = false
+export default {
+    data: () => ({
+        value: null,
+        valueConsistsOf: 'LEAF_PRIORITY',
+        options: [],
+        showLoading: 'loading...'
+    }),
+    props: {
+        oneData: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        },
+        //是否多选
+        multiple: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        },
+        appId: {
+            default() {
+                return ''
+            }
+        },
+        //判断媒体类型
+        request: {
+            type: String,
+            default() {
+                return '2,4'
+            }
+        },
+        //一级请求接口
+        reqUrl: {
+            type: String,
+            default() {
+                return '/ctop/projectMember/getProductProjectAndAccountList'
+            }
+        },
+        // 展示条数
+        limit: {
+            type: Number,
+            default() {
+                return 2
+            }
+        },
+        disabled: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        }
+    },
+    components: {
+        Treeselect
+    },
+    computed: {
+        selectedVal: {
+            get: function() {
+                return this.value
+            },
+            set: function(val) {
+                console.log(val)
+                this.$emit('input', val)
+                this.$emit('change', val)
+            }
+        }
+    },
+    watch: {
+        $route(n, o) {}
+        // appId: {
+        //     handler(n, o) {
+        //         console.log(n)
+        //         if (n.length == 0) {
+        //             if (this.multiple) {
+        //                 this.value = []
+        //             } else {
+        //                 this.value = undefined
+        //             }
+        //         } else {
+        //             this.value = this.appId
+        //         }
+        //     },
+        //     deep: true,
+        //     immediate: true
+        // }
+    },
+    methods: {
+        ...mapGetters(['nickname', 'avatar', 'userInfo']),
+        update(node, id) {
+            this.$emit('select-value', node)
+            // getAction('/ctop/userAllocation/getAccountListByProjectId', {
+            //   projectId: node.id,
+            // }).then((res) => {
+            //   // console.log(res)
+            //   if (res.code == 0) {
+            //     if (res.result.length > 0) {
+            //       node.children = res.result.map((item) => {
+            //         return {
+            //           id: item.accountId,
+            //           label: item.userName + '         ' + item.authName,
+            //         }
+            //       })
+            //     } else {
+            //     }
+            //   } else {
+            //     new Error('Failed to load options: network error.')
+            //   }
+            // })
+        },
+        emitValue() {
+            this.$emit('update:appId', this.value)
+        },
+        loadOptions({ action, parentNode, callback }) {
+            // Typically, do the AJAX stuff here.
+            // Once the server has responded,
+            // assign children options to the parent node & call the callback.
+            if (action === LOAD_CHILDREN_OPTIONS) {
+                // getAction('/ctop/userAllocation/getAccountListByProjectId', {
+                //   projectId: parentNode.id,
+                // }).then((res) => {
+                //   if (res.code == 0) {
+                //     if (res.result.length > 0) {
+                //       parentNode.children = res.result.map((item) => {
+                //         return {
+                //           id: item.accountId,
+                //           label: item.userName + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + item.authName,
+                //         }
+                //       })
+                //       callback()
+                //     } else {
+                //       parentNode.children = []
+                //       callback()
+                //     }
+                //   } else {
+                //     callback(new Error('Failed to load options: network error.'))
+                //   }
+                // })
+            }
+        },
+        getParticipateList() {
+            //我参与的
+            //   toutiaoParticipateList
+            var params = {}
+            params.userId = this.userInfo().id
+            postAction(this.reqUrl, params).then(res => {
+                if (res.success) {
+                    console.log(res)
+                    this.options = res.result.map(item => {
+                        return {
+                            id: item.productId + 'productId',
+                            label: item.productName,
+                            children: item.projectList
+                                ? item.projectList.map(i => {
+                                      return {
+                                          id: i.projectId,
+                                          label: i.projectName,
+                                          children: i.accountList
+                                              ? i.accountList.map(c => {
+                                                    return {
+                                                        id: c.accountId,
+                                                        label:
+                                                            (c.userName && c.userName.length == 2
+                                                                ? c.userName + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0'
+                                                                : c.userName && c.userName.length == 3
+                                                                ? c.userName + '\xa0\xa0\xa0\xa0'
+                                                                : c.userName) +
+                                                            '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' +
+                                                            c.accountId +
+                                                            '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' +
+                                                            c.authName
+                                                    }
+                                                })
+                                              : []
+                                      }
+                                  })
+                                : []
+                        }
+                    })
+                    if (this.oneData && !this.multiple) {
+                        var data = this.options.filter(item => {
+                            if (item.children && item.children.length > 0) {
+                                return { ...item }
+                            }
+                        })
+                        this.$emit('update:appId', data[0].children[0].id)
+                    }
+                }
+            })
+        }
+    },
+
+    mounted() {
+        this.getParticipateList()
+    }
+}
+</script>

+ 228 - 0
src/views/modules/autoLaunch/channelManage/components/treeSelectProject.vue

@@ -0,0 +1,228 @@
+<style lang="scss" scoped>
+.select-project {
+    width: 50%;
+}
+</style>
+<template>
+    <div class="select-project">
+        <treeselect
+            :multiple="multiple"
+            :disable-branch-nodes="!multiple"
+            :disable-fuzzy-matching="true"
+            :options="options"
+            :load-options="loadOptions"
+            placeholder="请选择项目以及账户"
+            v-model="selectedVal"
+            :value-consists-of="valueConsistsOf"
+            :auto-load-root-options="false"
+            @select="update"
+            @input="emitValue"
+            :limit="limit"
+            :default-options="true"
+            :disabled="disabled"
+            :limitText="
+                count => {
+                    return '隐藏' + count + '条'
+                }
+            "
+            noChildrenText="该项目下暂无账号"
+            :noOptionsText="showLoading"
+        />
+    </div>
+</template>
+
+<script>
+//   @open="getParticipateList"
+// import the component
+import Treeselect from '@riophae/vue-treeselect'
+// import the styles
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { LOAD_CHILDREN_OPTIONS } from '@riophae/vue-treeselect'
+
+import { getAction, postAction } from '@/api/manage'
+import { mapGetters } from 'vuex'
+// We just use `setTimeout()` here to simulate an async operation
+// instead of requesting a real API server for demo purpose.
+let called = false
+export default {
+    data: () => ({
+        value: null,
+        valueConsistsOf: 'LEAF_PRIORITY',
+        options: [],
+        showLoading: 'loading...'
+    }),
+    props: {
+        oneData: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        },
+        //是否多选
+        multiple: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        },
+        appId: {
+            type: [Number, Array, String],
+            default() {
+                return ""
+            }
+        },
+        //判断媒体类型
+        request: {
+            type: String,
+            default() {
+                return '2,4'
+            }
+        },
+        //一级请求接口
+        reqUrl: {
+            type: String,
+            default() {
+                return '/ctop/projectMember/getProductProjectList'
+            }
+        },
+        // 展示条数
+        limit: {
+            type: Number,
+            default() {
+                return 2
+            }
+        },
+        disabled: {
+            type: Boolean,
+            default() {
+                return false
+            }
+        }
+    },
+    components: {
+        Treeselect
+    },
+    computed: {
+        selectedVal: {
+            get: function() {
+                return this.value
+            },
+            set: function(val) {
+                console.log(val)
+                this.$emit('input', val)
+                this.$emit('change', val)
+            }
+        }
+    },
+    watch: {
+        $route(n, o) {},
+        // appId: {
+        //     handler(n, o) {
+        //         console.log(n)
+        //         if (n.length == 0) {
+        //             if (this.multiple) {
+        //                 this.value = []
+        //             } else {
+        //                 this.value = undefined
+        //             }
+        //         } else {
+        //             this.value = this.appId
+        //         }
+        //     },
+        //     deep: true,
+        //     immediate: true
+        // }
+    },
+    methods: {
+        ...mapGetters(['nickname', 'avatar', 'userInfo']),
+        update(node, id) {
+            this.$emit('select-value', node)
+            // getAction('/ctop/userAllocation/getAccountListByProjectId', {
+            //   projectId: node.id,
+            // }).then((res) => {
+            //   // console.log(res)
+            //   if (res.code == 0) {
+            //     if (res.result.length > 0) {
+            //       node.children = res.result.map((item) => {
+            //         return {
+            //           id: item.accountId,
+            //           label: item.userName + '         ' + item.authName,
+            //         }
+            //       })
+            //     } else {
+            //     }
+            //   } else {
+            //     new Error('Failed to load options: network error.')
+            //   }
+            // })
+        },
+        emitValue() {
+            // this.$emit('update:appId', this.value)
+        },
+        loadOptions({ action, parentNode, callback }) {
+            // Typically, do the AJAX stuff here.
+            // Once the server has responded,
+            // assign children options to the parent node & call the callback.
+            if (action === LOAD_CHILDREN_OPTIONS) {
+                // getAction('/ctop/userAllocation/getAccountListByProjectId', {
+                //   projectId: parentNode.id,
+                // }).then((res) => {
+                //   if (res.code == 0) {
+                //     if (res.result.length > 0) {
+                //       parentNode.children = res.result.map((item) => {
+                //         return {
+                //           id: item.accountId,
+                //           label: item.userName + '\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0' + item.authName,
+                //         }
+                //       })
+                //       callback()
+                //     } else {
+                //       parentNode.children = []
+                //       callback()
+                //     }
+                //   } else {
+                //     callback(new Error('Failed to load options: network error.'))
+                //   }
+                // })
+            }
+        },
+        getParticipateList() {
+            //我参与的
+            //   toutiaoParticipateList
+            var params = {}
+            params.userId = this.userInfo().id
+            postAction(this.reqUrl, params).then(res => {
+                if (res.success) {
+                    console.log(res)
+                    this.options = res.result.map(item => {
+                        return {
+                            id: item.productId + 'productId',
+                            label: item.productName,
+                            children: item.projectList
+                                ? item.projectList.map(i => {
+                                      return {
+                                          id: i.projectId,
+                                          label: i.projectName
+                                      }
+                                  })
+                                : []
+                        }
+                    })
+                    if (this.oneData && !this.multiple) {
+                        var data = this.options.filter(item => {
+                            if (item.children && item.children.length > 0) {
+                                return { ...item }
+                            }
+                        })
+                        this.$emit('update:appId', data[0].children[0].id)
+                    }
+                }
+            })
+        }
+    },
+
+    mounted() {
+        this.getParticipateList()
+    }
+}
+</script>

+ 365 - 0
src/views/modules/autoLaunch/channelManage/components/uploadFile.vue

@@ -0,0 +1,365 @@
+<style>
+</style>
+<style lang="scss" scoped>
+</style>
+<template>
+  <div class="upload-file">
+    <a-upload
+      name="file"
+      :multiple="multiple"
+      list-type="picture-card"
+      class="avatar-uploader"
+      action="https://media-1301855440.cos.ap-chongqing.myqcloud.com/"
+      :before-upload="beforeUpload"
+      @change="handleChange"
+      :accept="uploadType + '/' + (uploadType == 'image' ? 'jpeg,image/*' : 'mp4')"
+      :file-list="fileList"
+      :remove="removeFile"
+      @preview="handlePreview"
+      :disabled="loadingElse || disabled"
+    >
+      <div v-if="fileList.length < fileCount || fileList.length < 1">
+        <a-progress v-if="loadingElse" :percent="percent" :show-info="false" />
+        <div class="ant-upload-text">点击上传</div>
+      </div>
+    </a-upload>
+    <!-- <div style="color: red" v-if="sizeCheck">
+      {{ uploadType == 'image' ? '请上传jpg格式的图片,并且大小在2m之内' : '请上传mp4格式,大小在'+(size/1024)+'m之内' }}
+    </div> -->
+    <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
+      <img alt="example" style="width: 100%" :src="previewImage" v-if="uploadType == 'image'" />
+      <video :src="previewImage" v-else style="width: 100%" controls="controls">您的浏览器不支持 video 标签。</video>
+    </a-modal>
+  </div>
+</template>
+
+<script>
+import { stopOtherVideo, closeAllVideoFun } from '@/utils/videoControl' // 停止除当前外的其他视频播放,及停止所有视频播放的方法
+import { mapGetters } from 'vuex'
+import moment from 'moment'
+
+import BMF from 'browser-md5-file'
+
+import qs from 'qs'
+var COS = require('cos-js-sdk-v5')
+var cos = new COS({
+  SecretId: 'AKIDE6IpMi8fJQRCg1iuWzFajjRs43kbbets',
+  SecretKey: 'tXzuwMfplTTK3c9GFUyETilasvQfePu9',
+})
+var i = 0
+const oneKB = 1024
+
+function getBase64(file) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.readAsDataURL(file)
+    reader.onload = () => resolve(reader.result)
+    reader.onerror = (error) => reject(error)
+  })
+}
+export default {
+  name: 'upload-file',
+  components: {},
+  props: {
+    uploadType: {
+      //上传类型  值为  image/video,同时目录名称也是这样
+      type: String,
+      default() {
+        return 'image'
+      },
+    },
+    multiple: {
+      // true 可以批量上传  false 不可以
+      type: Boolean,
+      default() {
+        return true
+      },
+    },
+    fileCount: {
+      //最大上传数量
+      type: Number,
+      default() {
+        return 10
+      },
+    },
+    checkFile: {
+      //返回promise方法,判读文件是否上传过,进行上传拦截
+      type: Function,
+      default() {
+        return Promise.resolve()
+      },
+    },
+    disabled: {
+      type: Boolean,
+      default() {
+        return false
+      },
+    },
+    value: {
+      required: true,
+      default() {
+        return ''
+      },
+    },
+    size: {
+      type: Number,
+      default() {
+        if (this.sizeCheck) {
+          if (this.uploadType == 'image') {
+            return 2048
+          } else {
+            return 102400
+          }
+        }
+      },
+    },
+    sizeCheck: {
+      type: Boolean,
+      default() {
+        return true
+      },
+    },
+    onOversize: {
+      type: Function,
+      default() {
+        alert(`请选择${this.size}KB内的文件!`)
+      },
+    },
+  },
+  data() {
+    return {
+      fileList: [],
+      loadingElse: false,
+      imageUrl: '',
+      percent: 0,
+      previewVisible: false,
+      previewImage: '',
+    }
+  },
+  filters: {},
+  computed: {},
+  mounted() {},
+  watch: {
+    //    fileListAll
+    value: {
+      immediate: true,
+      handler(n, o) {
+        this.$nextTick(() => {
+          if (this.fileList && this.fileList.length == 0) {
+            if (typeof n == 'object') {
+              this.fileList = n.map((item, index) => {
+                return {
+                  uid: index + 1,
+                  name: index + 1,
+                  status: 'done',
+                  url: item,
+                }
+              })
+            } else {
+              if (n === '') {
+                this.fileList = []
+              } else {
+                this.fileList = []
+                var data = {
+                  uid: 1,
+                  name: '回显',
+                  status: 'done',
+                  url: n,
+                }
+                this.fileList.push(data)
+              }
+            }
+          } else {
+            if (n.length == 0 || n == '') {
+              this.clearAll()
+            }
+          }
+        })
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    handleCancel() {
+      closeAllVideoFun()
+      this.previewVisible = false
+    },
+    handleChange(info) {
+      if (info.file.status === 'uploading') {
+        this.loading = true
+        return
+      }
+      if (info.file.status === 'done') {
+        // Get this url from response in real world.
+        getBase64(info.file.originFileObj, (imageUrl) => {
+          this.imageUrl = imageUrl
+          this.loading = false
+        })
+      }
+    },
+    removeFile(file) {
+      // console.log(file)
+      var index = this.fileList.findIndex((v) => v.uid === file.uid)
+      this.fileList.splice(index, 1)
+      this.$emit('removeUpload', file)
+    },
+    async handlePreview(file) {
+      if (!file.url && !file.preview) {
+        file.preview = await getBase64(file.originFileObj)
+      }
+      this.previewImage = file.url || file.preview
+      this.previewVisible = true
+    },
+    beforeUpload(file) {
+      this.percent = 0
+      this.loadingElse = true
+      const fileOvesize = file.size > this.size * oneKB
+      console.log(file)
+      var image = new Image()
+      var reader = new FileReader()
+      var height = 0
+      var width = 0
+      reader.readAsDataURL(file)
+      //文件读取结束时间
+      reader.onload = function (e) {
+        image.src = reader.result
+      }
+      var that = this
+      //图片读取事件
+      image.onload = function () {
+        height = image.naturalHeight
+        width = image.naturalWidth
+      }
+      if (image.complete) {
+        height = image.naturalHeight
+        width = image.naturalWidth
+      }
+
+      setTimeout(() => {
+        if (height != 450 || width != 450) {
+          that.$message.error('请上传450*450大小的图片')
+          this.loadingElse = false
+          return
+        }
+        // console.log(fileOvesize)
+        if (fileOvesize && this.sizeCheck) {
+          this.onOversize()
+          this.loadingElse = false
+          return
+        } else {
+          if (file.type.includes('jpg') || file.type.includes('png') || file.type.includes('jpeg')) {
+            this.checkFile(file)
+              .then((res) => {
+                this.cosUpload(file)
+              })
+              .catch(() => {
+                this.loadingElse = false
+              })
+          } else {
+            this.$message.error('请上传正确格式')
+            this.loadingElse = false
+          }
+
+          return new Promise(function (resolve, reject) {
+            reject()
+          })
+        }
+      }, 200)
+    },
+    clearAll() {
+      this.fileList = []
+    },
+    cosUpload(file) {
+      var that = this
+      let date = new Date()
+      let y = date.getFullYear()
+      let MM = date.getMonth() + 1
+      MM = MM < 10 ? '0' + MM : MM
+      let d = date.getDate()
+      d = d < 10 ? '0' + d : d
+      var timeElse = new Date().getTime()
+      var arr = file.name.split('.')
+
+      var str = ''
+      for (let i = 0; i < arr.length; i++) {
+        if (i == arr.length - 1) {
+        } else {
+          if (i == arr.length - 2) {
+            str += arr[i]
+          } else {
+            str += arr[i] + '.'
+          }
+        }
+      }
+      // console.log(str + '-' + timeElse + '.' + arr[arr.length - 1])
+      cos.putObject(
+        {
+          Bucket: 'media-1301855440',
+          /* 必须 */
+          Region: 'ap-chongqing',
+          /* 存储桶所在地域,必须字段 */
+          Key: that.uploadType + '/' + y + '-' + MM + '-' + d + '/' + str + '-' + timeElse + '.' + arr[arr.length - 1],
+          /* 必须 */
+          StorageClass: 'STANDARD',
+          Body: file, // 上传文件对象
+          onProgress: function (progressData) {
+            //进度条方法
+
+            that.percent = progressData.percent * 100
+          },
+          onTaskReady: function (tid) {
+            // console.log('onTaskReady', tid);
+          },
+          onTaskStart: function (info) {
+            //开始上传
+            // console.log('onTaskStart', info);
+          },
+        },
+        function (err, data) {
+          if (err) {
+            that.loadingElse = false
+            that.$message.error('上传失败!!!' + err)
+            return
+          }
+          // alert('成功')
+          that.loadingElse = false
+          if (that.fileList.length < that.fileCount) {
+            that.fileList.push({
+              uid: file.name,
+              name: file.name,
+              status: 'done',
+              url: '//' + data.Location,
+            })
+
+            // that.$emit('overUpload', that.fileList.map(item => {
+            //                 return item.url
+            //               }))
+            that.$emit('overUpload', that.fileList)
+
+            if (that.multiple) {
+              that.$emit(
+                'update:value',
+                that.fileList.map((item) => {
+                  return item.url
+                })
+              )
+            } else {
+              that.$emit(
+                'update:value',
+                that.fileList.map((item) => {
+                  return item.url
+                })[0]
+              )
+            }
+          } else {
+            that.$message.error('上传已达上限' + that.fileCount + '张')
+          }
+        }
+      )
+    },
+  },
+}
+</script>
+<style scoped>
+@import '~@assets/less/common.less';
+</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1007 - 0
src/views/modules/autoLaunch/channelManage/createChannel.vue


Dosya farkı çok büyük olduğundan ihmal edildi
+ 303 - 837
src/views/modules/autoLaunch/channelManage/index.vue


+ 936 - 0
src/views/modules/autoLaunch/channelManage/itemList.vue

@@ -0,0 +1,936 @@
+<style lang="scss" scoped>
+/deep/.vue-treeselect__control {
+    line-height: 36px !important;
+}
+/deep/ .ant-collapse-item,
+/deep/.ant-collapse-content {
+    overflow: inherit !important;
+}
+.search-bar {
+    display: flex;
+    align-items: center;
+}
+.search-box p {
+    display: inline-block;
+    border: 1px solid #f2f2f2;
+    border-radius: 5px;
+    margin-right: 10px;
+    padding: 5px 10px;
+    cursor: pointer;
+}
+</style>
+<style scoped>
+.showNumBox {
+    margin: 10px 0;
+}
+.showNumBox .ant-card-body {
+    display: flex;
+    justify-content: center;
+}
+.showNumBox p,
+.showNumBox span {
+    text-align: center;
+    line-height: 40px;
+    font-size: 30px;
+    font-weight: 600;
+    color: red;
+    margin: 0;
+}
+.search-box .ant-card-body {
+    padding: 5px 15px;
+}
+.showNumBox .ant-card-body .styleElse {
+    line-height: 60px;
+    height: 40px;
+    font-size: 16px;
+    font-weight: 400;
+    margin-left: 10px;
+}
+.ant-modal-wrap .yincang .ant-modal-content .ant-modal-footer {
+    display: none !important;
+}
+.yincang .ant-col-6 p {
+    text-align: left;
+    color: red;
+    font-size: 16px;
+    font-weight: 500;
+}
+.ant-table-thead div {
+    font-weight: 700;
+}
+.tool-tip {
+    position: absolute;
+    top: 65px;
+    right: 10px;
+}
+</style>
+<style lang="less" scoped>
+.ant-card-head .ant-tabs-bar {
+    padding: 0;
+}
+.search-box .ant-card-body {
+    padding: 5px 15px;
+}
+.search-box {
+    padding: 15px 10px;
+}
+.search-box p {
+    display: inline-block;
+    border: 1px solid #f2f2f2;
+    border-radius: 5px;
+    margin-right: 10px;
+    padding: 5px 10px;
+    cursor: pointer;
+}
+.statistics .ant-col-6 .ant-card-body .styleElse {
+    line-height: 60px;
+    height: 40px;
+    font-size: 16px;
+    font-weight: 400;
+    margin-left: 10px;
+}
+.ant-modal-wrap .yincang .ant-modal-content .ant-modal-footer {
+    display: none !important;
+}
+.yincang .ant-col-6 p {
+    text-align: left;
+    color: red;
+    font-size: 16px;
+    font-weight: 500;
+}
+.ant-table-thead div {
+    font-weight: 700;
+}
+.tool-tip {
+    position: absolute;
+    top: 65px;
+    right: 10px;
+}
+.statistics {
+    .ant-card-bordered {
+        border: none;
+    }
+    .ant-card-head .ant-tabs {
+        clear: both;
+        margin-bottom: -13px;
+        color: rgba(0, 0, 0, 0.65);
+        font-weight: normal;
+        font-size: 14px;
+    }
+    .duibi {
+        position: absolute;
+        right: 24px;
+        top: 15px;
+    }
+    p {
+        margin-bottom: 0;
+    }
+}
+</style>
+
+<style lang="less" scoped>
+@import 'channelManage';
+</style>
+<template>
+    <div class="channelManage config-project">
+        <a-card>
+            <div class="table-page-search-wrapper">
+                <a-form layout="inline" :form="form" hide-required-mark :selfUpdate="true" @submit="handleQueryList">
+                    <a-row :gutter="24">
+                        <!-- <a-col :md="8" :sm="8" :xxl="6">
+                            <selectTable :projectId.sync="productId" ref="selectTable"></selectTable>
+                        </a-col> -->
+
+                        <a-col :md="8" :sm="8" :xxl="6">
+                            <selectTableProject
+                                :projectId.sync="projectId"
+                                :productId="productId"
+                                ref="selectTableProject"
+                            ></selectTableProject>
+                        </a-col>
+                        <!--  -->
+                        <a-col :md="8" :sm="8" :xxl="6">
+                            <a-form-item label="素材关键字">
+                                <a-input placeholder="请输入素材关键字" v-decorator="['keyword']"></a-input>
+                            </a-form-item>
+                        </a-col>
+                        <a-col :md="8" :sm="8" :xxl="6">
+                            <a-form-item label="单品名称">
+                                <a-input placeholder="请输入单品名称" v-decorator="['itemName']"></a-input>
+                            </a-form-item>
+                        </a-col>
+                        <a-col :md="8" :sm="8" :xxl="6">
+                            <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
+                                <a-button type="primary" icon="search" @click="handleQueryList">查询</a-button>
+                                <a-button type="primary" @click="handleResetForm" icon="reload" class="middle-button"
+                                    >重置</a-button
+                                >
+                                <a-button type="primary" @click="visibleAddItem = true">添加单品</a-button>
+                                <a-button type="primary" @click="visibleDownLoad = true" class="middle-button"
+                                    >批量导入单品</a-button
+                                >
+                            </span>
+                        </a-col>
+                    </a-row>
+                </a-form>
+            </div>
+        </a-card>
+
+        <a-row style="margin-bottom:15px;margin-top:12px">
+            <a-col :span="24">
+                <div class="tableBody" style="min-height:200px;padding-bottom: 50px">
+                    <a-table
+                        :columns="columns"
+                        :dataSource="dataSource"
+                        :pagination="false"
+                        :loading="dataLoading"
+                        bordered
+                        @change="tableChange"
+                        size="middle"
+                        style="word-break: break-all;font-size:16px;font-weight:600"
+                        id="outTable"
+                        ref="accountTable"
+                    >
+                        <span slot="state" slot-scope="text, record">
+                            <a-switch :checked="text" @change="handleSwitchChang($event, record)" />
+                        </span>
+                        <span slot="action" slot-scope="text, record">
+                            <a-popover
+                                v-model="record.visible"
+                                trigger="click"
+                                placement="topLeft"
+                                @visibleChange="visibleChange"
+                            >
+                                <span slot="title">关键词</span>
+                                <template slot="content" style="width:300px;">
+                                    <div style="margin:5px">
+                                        <span
+                                            ><a-input
+                                                v-model="keyWord"
+                                                size="small"
+                                                style="margin-right:20px;width:120px"
+                                        /></span>
+                                    </div>
+
+                                    <div>
+                                        <a-button
+                                            type="default"
+                                            @click="bidChangeCancel(record)"
+                                            size="small"
+                                            style="margin-right:10px"
+                                            >取消</a-button
+                                        >
+                                        <a-button type="primary" @click="bidChangeOk(record)" size="small" class="ok"
+                                            >修改</a-button
+                                        >
+                                    </div>
+                                </template>
+                                <a
+                                    @click="
+                                        () => {
+                                            record.visible = true
+                                        }
+                                    "
+                                    >编辑素材关键词</a
+                                >
+                            </a-popover>
+                        </span>
+                    </a-table>
+                    <a-pagination
+                        size="small"
+                        :showTotal="ipagination.showTotal"
+                        style="float: right;margin-top:10px"
+                        v-if="dataSource.length > 0"
+                        showQuickJumper
+                        :pageSize.sync="ipagination.pageSize"
+                        :total="ipagination.total"
+                        v-model="ipagination.current"
+                        @change="getDataSource"
+                    />
+                </div>
+            </a-col>
+        </a-row>
+
+        <a-modal
+            title="下载模板"
+            :width="800"
+            ref="modal"
+            :visible="visibleDownLoad"
+            @ok="handleOkImport"
+            @cancel="
+                visibleDownLoad = false
+                projectIdDownLoad = ''
+                cancel()
+            "
+            okText="提交"
+            :confirmLoading="downLoading"
+        >
+            <selectTableProject
+                :projectId.sync="projectIdDownLoad"
+                ref="selectTableProjectDownLoad"
+            ></selectTableProject>
+            <a-button type="primary" icon="plus" @click="handleOkDownLoad" style="margin-left:85px;margin-bottom:15px"
+                >导出单品模板</a-button
+            >
+            <a-form :form="formDownLoad">
+                <a-form-item
+                    label="上传文件"
+                    v-if="visibleDownLoad"
+                    :labelCol="{ xs: { span: 24 }, sm: { span: 5 } }"
+                    :wrapperCol="{ xs: { span: 24 }, sm: { span: 16 } }"
+                    class="require-class fileUpload"
+                    id="fileUpload"
+                >
+                    <a-upload
+                        name="file"
+                        :multiple="false"
+                        :headers="tokenHeader"
+                        :file-list="fileList"
+                        :showUploadList="false"
+                        :beforeUpload="beforeUpload"
+                        @change="handleChange"
+                        style="margin-right: 20px"
+                    >
+                        <a-button>
+                            <a-icon type="upload" />
+                            {{ loadDisabled ? '上传中,请稍后' : '文件上传' }}
+                        </a-button>
+                    </a-upload>
+                </a-form-item>
+            </a-form>
+        </a-modal>
+
+        <a-modal
+            title="添加单品"
+            :width="800"
+            ref="modal"
+            :visible="visibleAddItem"
+            @cancel="visibleAddItem = false"
+            @ok="handleOk"
+            :confirmLoading="confirmLoading"
+        >
+            <a-button type="primary" @click="addOneItem" style="margin-bottom:10px">添加单品信息</a-button>
+            <a-form-model ref="ruleForm" :model="addItemForm" v-bind="formItemLayout">
+                <a-collapse accordion v-model="activeKey" ref="collapse">
+                    <template v-for="(item, index) in addItemForm.items">
+                        <a-collapse-panel
+                            :header="item.itemName"
+                            style="background: #f9f9fa;border-radius: 4px;margin-bottom:15px;overflow: hidden;border: 1px solid #d9d9d9;"
+                        >
+                            <span slot="extra">
+                                <a-popconfirm :title="`确定删除吗?`" @confirm="deleteRule($event, item, index)">
+                                    <a-icon type="delete" @click="deleteIcon($event)" />
+                                </a-popconfirm>
+                            </span>
+                            <a-form-model-item
+                                label="项目"
+                                ref="projectId"
+                                :prop="'items.' + index + '.projectId'"
+                                :rules="[{ required: true, message: '请选择项目', trigger: 'blur' }]"
+                            >
+                                <!-- <a-input v-model="item.projectId" placeholder="请输入规则名称" /> -->
+                                <Treeselect v-model="item.projectId" style="width:100%" />
+                            </a-form-model-item>
+                            <a-form-model-item
+                                label="素材关键字"
+                                ref="keyword"
+                                :prop="'items.' + index + '.keyword'"
+                                :rules="[
+                                    { required: true, message: '请填写素材关键字', trigger: 'blur' },
+                                    { min: 1, message: '长度不得小于1', trigger: 'blur' }
+                                ]"
+                            >
+                                <a-input v-model="item.keyword" placeholder="请输入素材关键字" />
+                            </a-form-model-item>
+                            <a-form-model-item
+                                label="单品名称"
+                                ref="itemName"
+                                :prop="'items.' + index + '.itemName'"
+                                :rules="[
+                                    { required: true, message: '请填写单品名称', trigger: 'blur' },
+                                    { min: 1, message: '长度不得小于1', trigger: 'blur' }
+                                ]"
+                            >
+                                <a-input v-model="item.itemName" placeholder="请输入单品名称" />
+                            </a-form-model-item>
+                        </a-collapse-panel>
+                    </template>
+                </a-collapse>
+            </a-form-model>
+        </a-modal>
+    </div>
+</template>
+
+<script>
+import ARow from 'ant-design-vue/es/grid/Row'
+import ACol from 'ant-design-vue/es/grid/Col'
+import moment from 'moment'
+import { getAction, postAction, downFile, downFilePost } from '@/api/manage'
+import selectTable from '@/views/modules/material/comment/selectTable.vue'
+import selectTableProject from '@/views/modules/Statistics/components/selectPagination.vue'
+import Treeselect from './components/treeSelectProject.vue'
+import qs from 'qs'
+import { mapGetters } from 'vuex'
+import uploadFile from './components/uploadFile.vue'
+import Vue from 'vue'
+import { ACCESS_TOKEN } from '@/store/mutation-types'
+import $ from 'jquery'
+const fileSuffix = ['xlsx', 'xls', 'zip']
+let TTcolumns = [
+    {
+        title: '开关',
+        dataIndex: 'state',
+        scopedSlots: { customRender: 'state' },
+        align: 'center',
+        key: 'state'
+    },
+    {
+        title: '项目名称',
+        dataIndex: 'project_name',
+        scopedSlots: { customRender: 'project_name' },
+        align: 'center',
+        key: 'project_name'
+    },
+
+    {
+        title: '单品名称',
+        dataIndex: 'item_name',
+        scopedSlots: { customRender: 'item_name' },
+        align: 'center',
+        key: 'item_name'
+    },
+    {
+        title: '素材关键字',
+        dataIndex: 'keyword',
+        scopedSlots: { customRender: 'keyword' },
+        align: 'center',
+        key: 'keyword'
+    },
+    {
+        title: '操作',
+        dataIndex: 'action',
+        scopedSlots: { customRender: 'action' },
+        align: 'center',
+        key: 'action'
+    }
+]
+
+export default {
+    components: {
+        ACol,
+        ARow,
+        selectTable,
+        selectTableProject,
+        uploadFile,
+        Treeselect
+    },
+
+    data() {
+        return {
+            visibleAddItem: false,
+            confirmLoading: false,
+            activeKey: '0',
+            addItemForm: {
+                items: [{ projectId: '', keyWord: '', itemName: '默认名称-单品1' }]
+            },
+            formItemLayout: {
+                labelCol: { span: 4 },
+                wrapperCol: { span: 20 }
+            },
+            tokenHeader: { 'X-Access-Token': Vue.ls.get(ACCESS_TOKEN) },
+            fileList: [],
+            dataFile: null,
+            loadDisabled: false,
+            productId: '',
+            projectId: '',
+            accountId: '',
+            visibleChannel: false,
+            visibleDownLoad: false,
+            downLoading: false,
+            fileUrl: '',
+            projectIdDownLoad: '',
+            channelType: 'a',
+            createType: 1,
+            indexAll: 0,
+            spinning: false,
+            form: this.$form.createForm(this),
+            formDownLoad: this.$form.createForm(this),
+            exportloading: false,
+            dateRange: [moment().subtract(1, 'weeks'), moment()],
+            noImg: require('@/assets/noImg.png'),
+            validationRules: {
+                bid: [
+                    {
+                        required: true,
+                        message: '出价信息必须填写'
+                        // validator: this.validateBid,
+                    }
+                ]
+            },
+            ipagination: {
+                current: 1,
+                pageSize: 10,
+                //   pageSizeOptions: ['10', '20', '30'],
+                showTotal: (total, range) => {
+                    return range[0] + '-' + range[1] + ' 共' + total + '条'
+                },
+                showQuickJumper: true,
+                showSizeChanger: true,
+                pageSizeOptions: ['10', '30', '50'],
+                total: 0,
+                onChange: (current, pageSize) => {
+                    // 切换分页时的回调,
+                    // 当在页面定义change事件时,切记要把此处的事件清除,因为这两个事件重叠了,可能到时候会导致一些莫名的bug
+                    this.ipagination.current = current
+                    this.ipagination.pageSize = pageSize
+                }
+            },
+            value: 'valid',
+            KSaccountList: [],
+            TTaccountList: [],
+
+            KSdesignList: [],
+            TTdesignList: [],
+
+            operateDisabled: false,
+            duration: 1000,
+            //treeselect的参数
+            treeValue: [],
+            treeData: [],
+
+            type: 3,
+
+            operate: '',
+            designList: [],
+
+            tabKey: 1,
+
+            loading: false,
+            columns: [...TTcolumns],
+            dataSource: [],
+
+            tableTab: 1,
+
+            newPlan: 0,
+            effectPlan: 0,
+
+            openOptions: false,
+
+            target: 'createTime',
+            order: 'descend',
+            dataLoading: false,
+            url: '/channelItem/queryChannelItemList',
+            modifyPriceVisible: false,
+            modifyPriceLoading: false,
+            modifyPriceDetail: null,
+            accountVisible: false,
+            againLoading: false,
+            accountProjectDetail: null,
+            accountProjectList: [],
+            keyWord: undefined
+        }
+    },
+    methods: {
+        moment,
+        deleteIcon(event) {
+            event.stopPropagation()
+        },
+        deleteRule(event, item, index) {
+            event.stopPropagation()
+            this.addItemForm.items.splice(index, 1)
+        },
+        visibleChange() {
+            this.keyWord = undefined
+        },
+        bidChangeCancel(record) {
+            record.visible = false
+            this.keyWord = undefined
+        },
+        bidChangeOk(record) {
+            record.visible = false
+            const paramsData = {
+                id: record.id,
+                keyword: this.keyWord
+            }
+            getAction('/channelItem/updateChannelItems', paramsData)
+                .then(result => {
+                    if (result.code === 200) {
+                        record.visible = false
+                        this.keyWord = undefined
+                        const paramsData = this.form.getFieldsValue()
+                        this.handleGetTableList(paramsData)
+                    } else {
+                        this.$message.error(result.message)
+                    }
+                })
+                .catch(error => {
+                    console.log(error, 'eeee')
+                })
+        },
+        handleSwitchChang(checked, data) {
+            const paramsData = {
+                id: data.id,
+                state: checked ? 1 : 0
+            }
+            getAction('/channelItem/updateChannelItems', paramsData)
+                .then(result => {
+                    if (result.code === 200) {
+                        const paramsData = this.form.getFieldsValue()
+                        this.handleGetTableList(paramsData)
+                    } else {
+                        this.$message.error(result.message)
+                    }
+                })
+                .catch(error => {
+                    console.log(error, 'eeee')
+                })
+        },
+        overUpload(file) {},
+        handleChange() {
+            this.loadDisabled = false
+            this.$message.success('文件导入成功')
+        },
+        beforeUpload(file) {
+            // this.cosUpload(file)
+            // if (process.env.NODE_ENV == 'development') {
+            //   this.uploadAction = `/jeecg-boot/kuaishouAppPackage/importAppExcel?projectId=${this.projectId}&platform=${
+            //     this.appType == 'ios' ? +this.platform + 2 : +this.platform
+            //   }&imageUrl=${this.fileUrl}`
+            // } else if (process.env.NODE_ENV == 'debug') {
+            // } else if (process.env.NODE_ENV == 'production') {
+            //   this.uploadAction = `${this.axios.defaults.baseURL}/kuaishouAppPackage/importAppExcel?projectId=${
+            //     this.projectId
+            //   }&platform=${this.appType == 'ios' ? +this.platform + 2 : +this.platform}&imageUrl=${this.fileUrl}`
+            // }
+            // this.dataFile = file
+            this.dataFile = $('.fileUpload')
+                .eq(0)
+                .find('input')[0].files[0]
+            this.loadDisabled = true
+            const size = (Number(file.size) / (1024 * 1024)).toFixed(2)
+            const fileName = file.name
+            const fileStrArr = fileName.split('.')
+            const suffix = fileStrArr[fileStrArr.length - 1]
+
+            let result = fileSuffix.some(item => {
+                return item === suffix
+            })
+
+            if (!result) {
+                this.$message.error('不支持该文件类型')
+                this.loadDisabled = false
+                return false
+            }
+        },
+        ...mapGetters(['userInfo']),
+        toDetailList(item) {},
+
+        cpaBidValid(rule, value, callback) {
+            let z = /-/g
+            let a = /\//g
+            let d = /\./g
+            if (value === '' || value === undefined) {
+                callback('出价必须填写')
+            } else if (value.startsWith('.') || value.endsWith('.')) {
+                callback('请填写正确出价')
+            } else if (value.startsWith('-') || value.endsWith('-')) {
+                callback('请填写正确出价')
+            } else if (value.match(a) && value.match(z)) {
+                callback('不允许出现除"-"和"/"之外的所有字符,并且二者不可同时出现')
+            } else if (value.match(/[^0-9+-\/]/g)) {
+                callback('请填写正确出价')
+            } else {
+                callback()
+            }
+        },
+        addOneItem() {
+            this.addItemForm.items.push({
+                projectId: '',
+                keyWord: '',
+                itemName: '默认名称-单品' + (this.addItemForm.items.length + 1)
+            })
+        },
+        handleOk() {
+            this.$refs.ruleForm.validate(valid => {
+                if (valid) {
+                    this.confirmLoading = true
+                    postAction('/channelItem/addChannelItem', this.addItemForm.items).then(res => {
+                        this.confirmLoading = false
+                        if (res.success) {
+                            this.visibleAddItem = false
+                            this.addItemForm.items = [{ projectId: '', keyWord: '', itemName: '默认名称-单品1' }]
+                        } else {
+                            this.$message.error(res.message)
+                        }
+                    })
+                }
+            })
+        },
+        handleOkImport() {
+            this.downLoading = true
+            let params = {}
+            if (this.dataFile) {
+                let formData = new FormData()
+                formData.append('file', this.dataFile)
+
+                postAction('/channelItem/importItemExcel', formData).then(res => {
+                    this.downLoading = false
+                    if (res.code === 200) {
+                        this.$message.success('创建成功')
+                        this.visibleDownLoad = false
+                        this.projectIdDownLoad = ''
+                        this.handleResetForm()
+                    } else {
+                        this.$message.error(res.message)
+                    }
+                })
+            } else {
+                this.$message.error('请上传文件')
+            }
+
+            return
+        },
+        handleOkDownLoad() {
+            if (!!this.projectIdDownLoad) {
+                let params = {}
+                params.projectId = this.projectIdDownLoad
+                params.userId = this.userInfo().id
+                downFile('/channelItem/exportItemTemplate', params).then(res => {
+                    let blob = new Blob([res], {
+                        type: 'application/vnd.openxmlformats-officedocument.spre1adsheetml.sheet'
+                    })
+                    let downloadElement = document.createElement('a')
+                    let href = window.URL.createObjectURL(blob) //创建下载的链接
+                    downloadElement.href = href
+                    downloadElement.download = '单品模板.xlsx' //下载后文件名
+                    document.body.appendChild(downloadElement)
+                    downloadElement.click() //点击下载
+                    document.body.removeChild(downloadElement) //下载完成移除元素
+                    window.URL.revokeObjectURL(href) //释放掉blob对象
+                })
+            } else {
+                this.$message.error('请选择项目')
+            }
+        },
+        handleCancel() {
+            this.visibleChannel = false
+        },
+        addNewChannel() {
+            this.visibleChannel = true
+        },
+        getDataSource(current, pageSize) {
+            // 切换分页时的回调,
+            // 当在页面定义change事件时,切记要把此处的事件清除,因为这两个事件重叠了,可能到时候会导致一些莫名的bug
+            this.ipagination.current = current
+            this.ipagination.pageSize = pageSize
+            const paramsData = this.form.getFieldsValue()
+            this.handleGetTableList(paramsData)
+        },
+        handleQueryList(event) {
+            this.ipagination.current = 1
+            event.preventDefault()
+            const paramsData = this.form.getFieldsValue()
+            this.handleGetTableList(paramsData)
+        },
+        handleResetForm() {
+            this.ipagination.current = 1
+            this.form.resetFields()
+            this.$refs.selectTable.keyValue = ''
+            this.$refs.selectTable.getData()
+            this.$refs.selectTableProject.keyValue = ''
+            this.$refs.selectTableProject.getData()
+            const paramsData = this.form.getFieldsValue()
+            this.handleGetTableList(paramsData)
+        },
+        handleGetTableList(data) {
+            let paramsData = {
+                projectId: this.projectId,
+                keyword: data.keyword,
+                itemName: data.itemName,
+                pageNo: this.ipagination.current,
+                pageSize: this.ipagination.pageSize
+            }
+            this.dataSource = []
+            this.dataLoading = true
+            this.getAction(this.url, paramsData)
+                .then(result => {
+                    this.dataLoading = false
+                    if (result.code === 200) {
+                        this.dataSource =
+                            result.result.list.map(item => {
+                                return {
+                                    ...item,
+                                    visible: false
+                                }
+                            }) || []
+                        this.ipagination.total = result.result.total || 0
+                        // result.result.total
+                    } else {
+                        this.$message.error(result.message)
+                    }
+                })
+                .catch(error => {
+                    this.dataLoading = false
+                    console.log(error, 'eeee')
+                })
+        },
+        getAccountNoProject() {
+            this.projectId = undefined
+            const paramsData = this.form.getFieldsValue()
+            this.handleGetTableList(paramsData)
+        },
+        disabledDate(current) {
+            // Can not select days before today and today
+            return current && current > moment().endOf('day')
+        },
+        dateChange(date, dateString) {
+            // console.log(date,dateString)
+        },
+
+        filterOption(input, option) {
+            // console.log(input, option)
+            return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+        },
+        operateChange(value) {
+            console.log(value)
+            if (value) {
+                this.accountDisabled = true
+                this.accountId = ''
+            } else {
+                this.accountDisabled = false
+            }
+        },
+
+        tableChange(pagination, filters, sorter, { currentDataSource }) {
+            this.ipagination.current = pagination.current
+            this.ipagination.pageSize = pagination.pageSize
+            if (sorter.order) {
+                this.order = sorter.order ? sorter.order : 'descend'
+                this.target = sorter.columnKey
+                const paramsData = this.form.getFieldsValue()
+                this.handleGetTableList({
+                    ...paramsData,
+                    target: sorter.columnKey,
+                    order: sorter.order ? sorter.order : 'descend'
+                })
+            }
+        },
+
+        // 获取所有运营
+        getAllOperator(key) {
+            let url = '/sys/user/getAllUserListV1'
+            this.designList = []
+
+            getAction(url, {}).then(res => {
+                if (res.success) {
+                    this.designList = res.result
+                }
+            })
+        }
+    },
+    watch: {
+        productId: function(n, o) {
+            if (n != '') {
+                this.$refs.selectTableProject.keyValue = ''
+                this.$refs.selectTableProject.getData(n)
+            }
+        }
+    },
+    computed: {
+        newDate() {
+            var myDate = new Date()
+            myDate.getYear() //获取当前年份(2位)
+            var n = myDate.getFullYear() //获取完整的年份(4位,1970-????)
+            var m = myDate.getMonth() //获取当前月份(0-11,0代表1月)
+            var d = myDate.getDate() //获取当前日(1-31)
+            var day = myDate.getHours()
+            return day
+        }
+    },
+    mounted() {
+        const paramsData = this.form.getFieldsValue()
+        this.handleGetTableList(paramsData)
+        this.getAllOperator()
+    },
+    activated: function() {},
+
+    filters: {
+        toPercentage(val) {
+            if (typeof val != 'string') {
+                return (val * 100).toFixed(2) + '%'
+            } else {
+                return val
+            }
+        },
+        //保留两位小数并且变成货币格式
+        decimalsHandle(val) {
+            if (val && typeof val != 'string') {
+                if (val >= 0) {
+                    val = parseFloat(val).toFixed(2)
+                    let numberStr = val.toString()
+                    let str = numberStr.split('.')
+
+                    let str0 = str[0].split('').reverse()
+                    for (let i = 0; i < str0.length; i++) {
+                        if ((i + 1) % 4 === 0) {
+                            str0.splice(i, 0, ',')
+                        }
+                    }
+                    str0.reverse()
+                    let handleResult = ''
+                    for (let j = 0; j < str0.length; j++) {
+                        handleResult += str0[j]
+                    }
+
+                    return handleResult + '.' + str[1]
+                } else {
+                    val = parseFloat(val).toFixed(2)
+                    let numberStr = val.toString()
+                    let str = numberStr.split('.')
+
+                    let str0 = str[0]
+                        .substr(1)
+                        .split('')
+                        .reverse()
+                    for (let i = 0; i < str0.length; i++) {
+                        if ((i + 1) % 4 === 0) {
+                            str0.splice(i, 0, ',')
+                        }
+                    }
+                    str0.reverse()
+                    let handleResult = ''
+                    for (let j = 0; j < str0.length; j++) {
+                        handleResult += str0[j]
+                    }
+
+                    return '-' + handleResult + '.' + str[1]
+                }
+            } else if (typeof val == 'string') {
+                return val
+            } else {
+                return 0
+            }
+        },
+        //变成货币格式
+        formatCurrency(val) {
+            if (val) {
+                let numberStr = val.toString()
+                let str = numberStr.split('').reverse()
+                for (let i = 0; i < str.length; i++) {
+                    if ((i + 1) % 4 === 0) {
+                        str.splice(i, 0, ',')
+                    }
+                }
+                str.reverse()
+                let handleResult = ''
+                for (let j = 0; j < str.length; j++) {
+                    handleResult += str[j]
+                }
+                return handleResult
+            } else {
+                return '-'
+            }
+        }
+    }
+}
+</script>