forked from golang/hotime
重新索引
This commit is contained in:
parent
3c0946510e
commit
6ff34dc11b
BIN
iedc_go.exe
Normal file
BIN
iedc_go.exe
Normal file
Binary file not shown.
599
tpt/company/4ff190d28ca8a590422566171501384f/202303021717.json
Normal file
599
tpt/company/4ff190d28ca8a590422566171501384f/202303021717.json
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
{
|
||||||
|
"OtherCopyrightsInfo": null,
|
||||||
|
"PatentsInfo": null,
|
||||||
|
"ProfileTags": null,
|
||||||
|
"SoftwareCopyrightsInfo": null,
|
||||||
|
"TrademarksInfo": null,
|
||||||
|
"base": {
|
||||||
|
"allows": [
|
||||||
|
{
|
||||||
|
"docName": "特种设备使用许可登记",
|
||||||
|
"docNo": "起27川A01066(17)",
|
||||||
|
"endDate": "2017-11-28 00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docName": "食品经营许可证",
|
||||||
|
"docNo": "JY35101080132152",
|
||||||
|
"endDate": "2020-03-09 00:00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docName": "食品经营许可证",
|
||||||
|
"docNo": "JY35101090132017",
|
||||||
|
"endDate": "2018-11-14 00:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"base": {
|
||||||
|
"OtherCopyrightsInfo": 0,
|
||||||
|
"PatentsInfo": 0,
|
||||||
|
"ProfileTags": null,
|
||||||
|
"SoftwareCopyrightsInfo": 0,
|
||||||
|
"TrademarksInfo": 0,
|
||||||
|
"allows": 3,
|
||||||
|
"authority": "成华市场监督管理局",
|
||||||
|
"branches": 7,
|
||||||
|
"businessDateFrom": "2000-07-05 16:00:00",
|
||||||
|
"businessDateTo": "2029-12-30 16:00:00",
|
||||||
|
"businessScope": "混凝土的生产、销售及技术咨询服务;混凝土设备的制造、安装、维修、租赁、销售;批发、零售建筑材料(不含危险化学品);汽车货运;仓储(不含危险化学品)。(依法须经批准的项目,经相关部门批准后方可开展经营活动)",
|
||||||
|
"capital": "17900.000000万人民币",
|
||||||
|
"changes": 53,
|
||||||
|
"companyAddress": "四川省成都市成华区成华大道二段298号",
|
||||||
|
"companyCode": "510000000052001",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司",
|
||||||
|
"companyStatus": "存续(在营、开业、在册)",
|
||||||
|
"companyType": "有限责任公司(非自然人投资或控股的法人独资)",
|
||||||
|
"contactInfo": {
|
||||||
|
"email": "18080039821@163.com",
|
||||||
|
"phoneNumber": "028-84125243"
|
||||||
|
},
|
||||||
|
"creditNo": "91510000720822440A",
|
||||||
|
"employees": 9,
|
||||||
|
"establishDate": "2000-07-06 00:00:00",
|
||||||
|
"exceptions": 0,
|
||||||
|
"industry": {
|
||||||
|
"industryL1Name": "制造业",
|
||||||
|
"industryL2Name": "非金属矿物制品业"
|
||||||
|
},
|
||||||
|
"isOnStock": "0",
|
||||||
|
"issueDate": "2022-03-16 00:00:00",
|
||||||
|
"keyNo": "b1eba21d8bddf3585f6cf025a3a9e99f",
|
||||||
|
"legalPerson": "刘华东",
|
||||||
|
"liquidation": null,
|
||||||
|
"mPledges": 0,
|
||||||
|
"orgCode": "720822440",
|
||||||
|
"originalName": 1,
|
||||||
|
"partners": 1,
|
||||||
|
"pledges": 0,
|
||||||
|
"province": "SC",
|
||||||
|
"punishes": 0,
|
||||||
|
"revokeDate": null,
|
||||||
|
"shiXinItems": 0,
|
||||||
|
"spotChecks": 1,
|
||||||
|
"stockNumber": null,
|
||||||
|
"stockType": null,
|
||||||
|
"taxCreditltems": 11,
|
||||||
|
"updatedDate": null,
|
||||||
|
"zhiXingItems": 0
|
||||||
|
},
|
||||||
|
"branches": [
|
||||||
|
{
|
||||||
|
"authority": "青白江区市场监督管理局",
|
||||||
|
"companyCode": "510113000060671",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司青白江分公司",
|
||||||
|
"creditNo": "915101130525038228",
|
||||||
|
"legalPerson": "彭勇军"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "崇州市市场监督管理局",
|
||||||
|
"companyCode": "510184000070938",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司崇州分公司",
|
||||||
|
"creditNo": "915101840961482443",
|
||||||
|
"legalPerson": "张林"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "彭州市市场和质量监督管理局",
|
||||||
|
"companyCode": "510182000048228",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司彭州混凝土搅拌站",
|
||||||
|
"creditNo": null,
|
||||||
|
"legalPerson": "王泽良"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "资阳市雁江区市场监督管理局",
|
||||||
|
"companyCode": "512000000033062",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司资阳分公司",
|
||||||
|
"creditNo": "91512002558214286K",
|
||||||
|
"legalPerson": "刘华东"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "成都市双流区市场监督管理局",
|
||||||
|
"companyCode": "510122000041658",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司双流混凝土搅拌站",
|
||||||
|
"creditNo": "91510122686311953C",
|
||||||
|
"legalPerson": "张林"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "成华市场监督管理局",
|
||||||
|
"companyCode": "510000000161152",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司水泥供应站",
|
||||||
|
"creditNo": "9151000072983742XM",
|
||||||
|
"legalPerson": "徐虹"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"authority": "成都市新都区市场监督管理局",
|
||||||
|
"companyCode": "510125000048152",
|
||||||
|
"companyName": "四川华西绿舍建材有限公司大丰混凝土搅拌站",
|
||||||
|
"creditNo": "91510114725389797P",
|
||||||
|
"legalPerson": "黄红俊"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"changeAfter": "有限责任公司(法人独资)",
|
||||||
|
"changeBefore": "有限责任公司",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "企业类型变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "经营类别:其它类",
|
||||||
|
"changeBefore": "经营类别:",
|
||||||
|
"changeDate": "2012-10-12 00:00:00",
|
||||||
|
"changeField": "其他变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "曾斌 敬尧 刘华东 肖波 李先勇",
|
||||||
|
"changeBefore": "黄平 张蓉川 马林 刘华东 马林 曾斌",
|
||||||
|
"changeDate": "2021-10-28 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案(董事、监事、经理等)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称: 四川华西集团有限公司; 出资额: 4500; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeBefore": "投资方名称: 四川省第三建筑工程公司; 出资额: 360; 百分比: 8; 股东类型: 企业法人\n投资方名称: 四川省第一建筑工程公司; 出资额: 490; 百分比: 10.89; 股东类型: 企业法人\n投资方名称: 四川省第六建筑工程公司; 出资额: 720; 百分比: 16; 股东类型: 企业法人\n投资方名称: 四川华西集团有限公司; 出资额: 2930; 百分比: 65.11; 股东类型: 企业法人",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "股东或股份发起人改变姓名或名称变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "有限责任公司",
|
||||||
|
"changeBefore": "其他有限责任公司",
|
||||||
|
"changeDate": "2008-04-24 00:00:00",
|
||||||
|
"changeField": "市场主体类型变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称:四川华西集团有限公司;出资日期:2013-05-23",
|
||||||
|
"changeBefore": "投资方名称:四川华西集团有限公司;出资日期:2011-06-24",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "出资日期变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2010-08-13 00:00:00",
|
||||||
|
"changeField": "负责人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:谢晓岚;证件类型:中华人民共和国居民身份证;证件号码:*****;联络人住所:;联络人联系电话:180****9821;联络人移动电话:180****9821;联络人电子邮件:180*********;邮政编码:;",
|
||||||
|
"changeBefore": "姓名:谢晓岚;证件类型:;证件号码:*****;联络人住所:;联络人联系电话:180****9821;联络人移动电话:180****9821;联络人电子邮件:;邮政编码:;",
|
||||||
|
"changeDate": "2015-10-10 00:00:00",
|
||||||
|
"changeField": "联络人员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:马林,职务:董事,证件号码:***",
|
||||||
|
"changeBefore": "姓名:马林,职务:董事兼总经理,证件号码:***",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "其他事项备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "实收资本:15900",
|
||||||
|
"changeBefore": "实收资本:15000",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "实收资本变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "15000",
|
||||||
|
"changeBefore": "4500",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "注册资本变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "有限责任公司(法人独资)",
|
||||||
|
"changeBefore": "有限责任公司",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "市场主体类型变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "",
|
||||||
|
"changeBefore": "无",
|
||||||
|
"changeDate": "2022-03-16 00:00:00",
|
||||||
|
"changeField": "章程备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:黄平,职务:董事,证件号码:***",
|
||||||
|
"changeBefore": "姓名:彭明先,职务:董事,证件号码:***",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "其他事项备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:曾斌,职务:董事,证件号码:***",
|
||||||
|
"changeBefore": "姓名:张军,职务:董事,证件号码:***",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "其他事项备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "住所: 成都市成华区成华大道二段298号; 邮政编码: 610051; 电话: 4116411住所所在地:",
|
||||||
|
"changeBefore": "住所: 成都市跳蹬河崔家店路98号; 邮政编码: 610051; 电话: 4116411住所所在地:",
|
||||||
|
"changeDate": "2008-04-24 00:00:00",
|
||||||
|
"changeField": "地址变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "负责人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "敬尧",
|
||||||
|
"changeBefore": "刘华东",
|
||||||
|
"changeDate": "2021-10-28 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案(董事、监事、经理等)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称: 四川华西集团有限公司; 出资额: 15000; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeBefore": "投资方名称: 四川华西集团有限公司; 出资额: 4500; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "投资人(股权)变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册资本(金):15900",
|
||||||
|
"changeBefore": "注册资本(金):15000",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "注册资本(金)变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "翁建民 *** 备案手机:***",
|
||||||
|
"changeBefore": "谢晓岚 *** 备案手机:***",
|
||||||
|
"changeDate": "2018-04-17 00:00:00",
|
||||||
|
"changeField": "联络员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "17900.000000万人民币",
|
||||||
|
"changeBefore": "15900万人民币",
|
||||||
|
"changeDate": "2022-03-16 00:00:00",
|
||||||
|
"changeField": "注册资本变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称: 四川华西集团有限公司; 出资额: 15000; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeBefore": "投资方名称: 四川华西集团有限公司; 出资额: 4500; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "股东或股份发起人改变姓名或名称变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "15000",
|
||||||
|
"changeBefore": "4500",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "注册资本(金)变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:谢晓岚;证件类型:中华人民共和国居民身份证;证件号码:***;联络人住所:;联络人联系电话:***;联络人移动电话:***;联络人电子邮件:***@163.com;邮政编码:;",
|
||||||
|
"changeBefore": "姓名:谢晓岚;证件类型:;证件号码:***;联络人住所:;联络人联系电话:***;联络人移动电话:***;联络人电子邮件:;邮政编码:;",
|
||||||
|
"changeDate": "2015-10-10 00:00:00",
|
||||||
|
"changeField": "联络员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称: 四川华西集团有限公司; 出资额: 4500; 百分比: 100; 股东类型: 企业法人",
|
||||||
|
"changeBefore": "投资方名称: 四川省第三建筑工程公司; 出资额: 360; 百分比: 8; 股东类型: 企业法人投资方名称: 四川省第一建筑工程公司; 出资额: 490; 百分比: 10.89; 股东类型: 企业法人投资方名称: 四川省第六建筑工程公司; 出资额: 720; 百分比: 16; 股东类型: 企业法人投资方名称: 四川华西集团有限公司; 出资额: 2930; 百分比: 65.11; 股东类型: 企业法人",
|
||||||
|
"changeDate": "2010-05-19 00:00:00",
|
||||||
|
"changeField": "投资人(股权)变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称:四川华西集团有限公司;出资日期:2013-05-23",
|
||||||
|
"changeBefore": "投资方名称:四川华西集团有限公司;出资日期:2011-06-24",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "股东或股份发起人改变姓名或名称变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "15000",
|
||||||
|
"changeBefore": "4500",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "实收资本变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:马林,证件类型:居民身份证,证件号码:*****",
|
||||||
|
"changeBefore": "姓名:王泽良,证件类型:居民身份证,证件号码:*****",
|
||||||
|
"changeDate": "2015-10-10 00:00:00",
|
||||||
|
"changeField": "负责人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "刘华东",
|
||||||
|
"changeBefore": "马林",
|
||||||
|
"changeDate": "2021-10-28 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "马林、刘华东",
|
||||||
|
"changeBefore": "李登龙、颜有光",
|
||||||
|
"changeDate": "2013-01-06 00:00:00",
|
||||||
|
"changeField": "章程备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "四川省成都市成华区成华大道二段298号",
|
||||||
|
"changeBefore": "成都市成华区成华大道二段298号",
|
||||||
|
"changeDate": "2022-03-16 00:00:00",
|
||||||
|
"changeField": "地址变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: 注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2011-06-01 00:00:00",
|
||||||
|
"changeField": "负责人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "谢晓岚 *** 备案手机:***",
|
||||||
|
"changeBefore": "翁建民 *** 备案手机:***",
|
||||||
|
"changeDate": "2021-10-28 00:00:00",
|
||||||
|
"changeField": "联络员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称:四川华西集团有限公司;出资额:15900",
|
||||||
|
"changeBefore": "投资方名称:四川华西集团有限公司;出资额:15000",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "出资额变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "2018-04-02因修改章程而备案",
|
||||||
|
"changeBefore": "无",
|
||||||
|
"changeDate": "2018-04-17 00:00:00",
|
||||||
|
"changeField": "章程备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:马林,证件类型:居民身份证,证件号码:***",
|
||||||
|
"changeBefore": "姓名:王泽良,证件类型:居民身份证,证件号码:***",
|
||||||
|
"changeDate": "2015-10-10 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "王泽良",
|
||||||
|
"changeBefore": "(冻结)梁勇",
|
||||||
|
"changeDate": "2012-10-12 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "四川华西绿舍建材有限公司",
|
||||||
|
"changeBefore": "四川华西混凝土工程有限公司",
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"changeField": "名称变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2010-08-13 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "投资方名称:四川华西集团有限公司;出资额:15900",
|
||||||
|
"changeBefore": "投资方名称:四川华西集团有限公司;出资额:15000",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "股东或股份发起人改变姓名或名称变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "龙鹤 刘华东 李先勇 李登龙 许世富",
|
||||||
|
"changeBefore": "李先勇 敬尧 曾斌 肖波 刘华东",
|
||||||
|
"changeDate": "2022-03-16 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案(董事、监事、经理等)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:谢晓岚;证件类型:中华人民共和国居民身份证;证件号码:*****;联络人住所:;联络人联系电话:*****;联络人移动电话:*****;联络人电子邮件:*****;邮政编码:;",
|
||||||
|
"changeBefore": "姓名:谢晓岚;证件类型:;证件号码:*****;联络人住所:;联络人联系电话:*****;联络人移动电话:*****;联络人电子邮件:;邮政编码:;",
|
||||||
|
"changeDate": "2015-10-10 00:00:00",
|
||||||
|
"changeField": "联络人员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册资本(金):15900",
|
||||||
|
"changeBefore": "注册资本(金):15000",
|
||||||
|
"changeDate": "2013-05-23 00:00:00",
|
||||||
|
"changeField": "注册资本变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "经营类别:其它类",
|
||||||
|
"changeBefore": "经营类别:",
|
||||||
|
"changeDate": "2012-10-12 00:00:00",
|
||||||
|
"changeField": "其他事项备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:曾斌,职务:董事,证件号码:*****",
|
||||||
|
"changeBefore": "姓名:张军,职务:董事,证件号码:*****",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:马林,职务:董事,证件号码:*****",
|
||||||
|
"changeBefore": "姓名:马林,职务:董事兼总经理,证件号码:*****",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "住所: 成都市成华区成华大道二段298号; 邮政编码: 610051; 电话: 4116411住所所在地:",
|
||||||
|
"changeBefore": "住所: 成都市跳蹬河崔家店路98号; 邮政编码: 610051; 电话: 4116411住所所在地:",
|
||||||
|
"changeDate": "2008-04-24 00:00:00",
|
||||||
|
"changeField": "住所变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "姓名:黄平,职务:董事,证件号码:*****",
|
||||||
|
"changeBefore": "姓名:彭明先,职务:董事,证件号码:*****",
|
||||||
|
"changeDate": "2016-04-11 00:00:00",
|
||||||
|
"changeField": "高级管理人员备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "四川华西集团有限公司 出资 17900万人民币;",
|
||||||
|
"changeBefore": "四川华西集团有限公司 出资 15900万人民币;",
|
||||||
|
"changeDate": "2022-03-16 00:00:00",
|
||||||
|
"changeField": "其他事项备案"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "有限责任公司",
|
||||||
|
"changeBefore": "其他有限责任公司",
|
||||||
|
"changeDate": "2008-04-24 00:00:00",
|
||||||
|
"changeField": "企业类型变更"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"changeAfter": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeBefore": "注册号: ; 企业名称: ; 法定代表人: 成都市二仙桥西路6号; 住所: 四川华西混凝土工程有限公司水泥供应站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市双流县西航港街道九龙湖社区; 住所: 四川华西混凝土工程有限公司双流搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 成都市新都区大丰; 住所: 四川华西混凝土工程有限公司第一搅拌站; 核准日期: 梁勇; 登记机关: \n注册号: ; 企业名称: ; 法定代表人: 彭州市隆丰镇红光村; 住所: 四川华西混凝土工程有限公司彭州混凝土搅拌站; 核准日期: 梁勇; 登记机关:",
|
||||||
|
"changeDate": "2011-06-01 00:00:00",
|
||||||
|
"changeField": "法定代表人变更"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"contactInfo": {
|
||||||
|
"email": "18080039821@163.com",
|
||||||
|
"phoneNumber": "028-84125243"
|
||||||
|
},
|
||||||
|
"employees": [
|
||||||
|
{
|
||||||
|
"employeeName": "王占虞",
|
||||||
|
"position": "监事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "刘静",
|
||||||
|
"position": "监事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "刘华东",
|
||||||
|
"position": "董事长"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "张世云",
|
||||||
|
"position": "监事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "敬尧",
|
||||||
|
"position": "总经理"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "李先勇",
|
||||||
|
"position": "董事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "龙鹤",
|
||||||
|
"position": "董事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "许世富",
|
||||||
|
"position": "董事"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"employeeName": "李登龙",
|
||||||
|
"position": "董事"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exceptions": [],
|
||||||
|
"industry": {
|
||||||
|
"industryL1Name": "制造业",
|
||||||
|
"industryL2Name": "非金属矿物制品业"
|
||||||
|
},
|
||||||
|
"liquidation": null,
|
||||||
|
"mPledges": [],
|
||||||
|
"originalName": [
|
||||||
|
{
|
||||||
|
"changeDate": "2011-07-19 00:00:00",
|
||||||
|
"name": "四川华西混凝土工程有限公司"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"partners": [
|
||||||
|
{
|
||||||
|
"capiDate": "2013-04-24 16:00:00",
|
||||||
|
"investName": "货币",
|
||||||
|
"investType": "货币",
|
||||||
|
"shoudDate": "2021-02-09 16:00:00",
|
||||||
|
"stockCapital": "17900.0万元",
|
||||||
|
"stockName": "四川华西集团有限公司",
|
||||||
|
"stockPercent": "1.0000",
|
||||||
|
"stockRealcapital": "15900.0万元",
|
||||||
|
"stockType": "企业法人"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pledges": [],
|
||||||
|
"punishes": [],
|
||||||
|
"shiXinItems": [],
|
||||||
|
"spotChecks": [
|
||||||
|
{
|
||||||
|
"consequence": "基本符合",
|
||||||
|
"date": "2021-11-18 00:00:00",
|
||||||
|
"executiveOrg": "成都高新技术产业开发区市场监督管理局",
|
||||||
|
"no": "1",
|
||||||
|
"remark": null,
|
||||||
|
"type": "检查"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"taxCreditltems": [
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "510108720822440",
|
||||||
|
"year": "2014"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2016"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2017"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2018"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2019"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2020"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "",
|
||||||
|
"year": "2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": "A",
|
||||||
|
"taxPayerName": "四川华西绿舍建材有限公司",
|
||||||
|
"taxPayerNo": "91510000720822440A",
|
||||||
|
"year": "2021"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zhiXingItems": []
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
tpt/excel/2022/10/31/2ed74060b61ad178354c7d2f9e66db28.xlsx
Normal file
BIN
tpt/excel/2022/10/31/2ed74060b61ad178354c7d2f9e66db28.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2022/10/31/f107c4ab38387ab9c31a232ab4079c70.xlsx
Normal file
BIN
tpt/excel/2022/10/31/f107c4ab38387ab9c31a232ab4079c70.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2022/12/27/70b30dc24f79d17d1833e64fca452483.xlsx
Normal file
BIN
tpt/excel/2022/12/27/70b30dc24f79d17d1833e64fca452483.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2022/12/27/e247c46c957bb5804408a9f53146b999.xlsx
Normal file
BIN
tpt/excel/2022/12/27/e247c46c957bb5804408a9f53146b999.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2022/12/27/f551c6d8795634265229314c6bfbaff8.xlsx
Normal file
BIN
tpt/excel/2022/12/27/f551c6d8795634265229314c6bfbaff8.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/01/b895c5fffc9f9e2fcc7d58a1d454389f.xlsx
Normal file
BIN
tpt/excel/2023/03/01/b895c5fffc9f9e2fcc7d58a1d454389f.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/1051674c274bae0bd6ddfff9ac95bf36.xlsx
Normal file
BIN
tpt/excel/2023/03/03/1051674c274bae0bd6ddfff9ac95bf36.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/3313584ebde0dd0011f02089886490b9.xlsx
Normal file
BIN
tpt/excel/2023/03/03/3313584ebde0dd0011f02089886490b9.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/3a7201b48735b2a779d03f29b918e76e.xlsx
Normal file
BIN
tpt/excel/2023/03/03/3a7201b48735b2a779d03f29b918e76e.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/6f599baf25a65bbbc7a8178549308ad6.xlsx
Normal file
BIN
tpt/excel/2023/03/03/6f599baf25a65bbbc7a8178549308ad6.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/b3cfd47023499429f669c85cdbc6042c.xlsx
Normal file
BIN
tpt/excel/2023/03/03/b3cfd47023499429f669c85cdbc6042c.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/c001c64c1fed6a6c68cf743497ab4894.xlsx
Normal file
BIN
tpt/excel/2023/03/03/c001c64c1fed6a6c68cf743497ab4894.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/d3cf0596a1f725b16398f951663e833d.xlsx
Normal file
BIN
tpt/excel/2023/03/03/d3cf0596a1f725b16398f951663e833d.xlsx
Normal file
Binary file not shown.
BIN
tpt/excel/2023/03/03/fb03d74ed8e37a8168fd245d13cbfaea.xlsx
Normal file
BIN
tpt/excel/2023/03/03/fb03d74ed8e37a8168fd245d13cbfaea.xlsx
Normal file
Binary file not shown.
BIN
tpt/file/temp/question2023-02-28-15-30-52.xlsx
Normal file
BIN
tpt/file/temp/question2023-02-28-15-30-52.xlsx
Normal file
Binary file not shown.
BIN
tpt/file/temp/question2023-02-28-15-31-08.xlsx
Normal file
BIN
tpt/file/temp/question2023-02-28-15-31-08.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/downloadQuestion8.xlsx
Normal file
BIN
tpt/temp/downloadQuestion8.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/home15.xlsx
Normal file
BIN
tpt/temp/home15.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/home3.xlsx
Normal file
BIN
tpt/temp/home3.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/home4.xlsx
Normal file
BIN
tpt/temp/home4.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/industry1x3.xlsx
Normal file
BIN
tpt/temp/industry1x3.xlsx
Normal file
Binary file not shown.
BIN
tpt/temp/industry40x15.xlsx
Normal file
BIN
tpt/temp/industry40x15.xlsx
Normal file
Binary file not shown.
4
vendor/code.hoteas.com/golang/hotime/.gitignore
generated
vendored
Normal file
4
vendor/code.hoteas.com/golang/hotime/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/.idea/*
|
||||||
|
.idea
|
||||||
|
/example/config/app.json
|
||||||
|
/example/tpt/demo/
|
||||||
0
vendor/code.hoteas.com/golang/hotime/.tgitconfig
generated
vendored
Normal file
0
vendor/code.hoteas.com/golang/hotime/.tgitconfig
generated
vendored
Normal file
208
vendor/code.hoteas.com/golang/hotime/LICENSE
generated
vendored
Normal file
208
vendor/code.hoteas.com/golang/hotime/LICENSE
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
Apache License
|
||||||
|
|
||||||
|
Version 2.0, January 2004
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
|
||||||
|
AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction, and distribution
|
||||||
|
as defined by Sections 1 through 9 of that document.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||||
|
owner that is granting the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||||
|
that control, are controlled by, or are under common control with that entity.
|
||||||
|
For the purposes of that definition, "control" means (i) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity, whether
|
||||||
|
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
|
||||||
|
of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
|
||||||
|
granted by that License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications, including
|
||||||
|
but not limited to software source code, documentation source, and configuration
|
||||||
|
files.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical transformation
|
||||||
|
or translation of a Source form, including but not limited to compiled object
|
||||||
|
code, generated documentation, and conversions to other media types.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or Object form,
|
||||||
|
made available under the License, as indicated by a copyright notice that
|
||||||
|
is included in or attached to the work (an example is provided in the Appendix
|
||||||
|
below).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object form,
|
||||||
|
that is based on (or derived from) the Work and for which the editorial revisions,
|
||||||
|
annotations, elaborations, or other modifications represent, as a whole, an
|
||||||
|
original work of authorship. For the purposes of that License, Derivative
|
||||||
|
Works shall not include works that remain separable from, or merely link (or
|
||||||
|
bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including the original version
|
||||||
|
of the Work and any modifications or additions to that Work or Derivative
|
||||||
|
Works thereof, that is intentionally submitted to Licensor for inclusion in
|
||||||
|
the Work by the copyright owner or by an individual or Legal Entity authorized
|
||||||
|
to submit on behalf of the copyright owner. For the purposes of that definition,
|
||||||
|
"submitted" means any form of electronic, verbal, or written communication
|
||||||
|
sent to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems, and
|
||||||
|
issue tracking systems that are managed by, or on behalf of, the Licensor
|
||||||
|
for the purpose of discussing and improving the Work, but excluding communication
|
||||||
|
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||||
|
owner as "Not a Contribution."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||||
|
of whom a Contribution has been received by Licensor and subsequently incorporated
|
||||||
|
within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of that
|
||||||
|
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
||||||
|
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
|
||||||
|
the Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of that License,
|
||||||
|
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in that section) patent
|
||||||
|
license to make, have made, use, offer to sell, sell, import, and otherwise
|
||||||
|
transfer the Work, where such license applies only to those patent claims
|
||||||
|
licensable by such Contributor that are necessarily infringed by their Contribution(s)
|
||||||
|
alone or by combination of their Contribution(s) with the Work to which such
|
||||||
|
Contribution(s) was submitted. If You institute patent litigation against
|
||||||
|
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that the Work or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses granted to You
|
||||||
|
under that License for that Work shall terminate as of the date such litigation
|
||||||
|
is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||||
|
Derivative Works thereof in any medium, with or without modifications, and
|
||||||
|
in Source or Object form, provided that You meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or Derivative Works a copy
|
||||||
|
of that License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices stating that
|
||||||
|
You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works that You distribute,
|
||||||
|
all copyright, patent, trademark, and attribution notices from the Source
|
||||||
|
form of the Work, excluding those notices that do not pertain to any part
|
||||||
|
of the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
||||||
|
then any Derivative Works that You distribute must include a readable copy
|
||||||
|
of the attribution notices contained within such NOTICE file, excluding those
|
||||||
|
notices that do not pertain to any part of the Derivative Works, in at least
|
||||||
|
one of the following places: within a NOTICE text file distributed as part
|
||||||
|
of the Derivative Works; within the Source form or documentation, if provided
|
||||||
|
along with the Derivative Works; or, within a display generated by the Derivative
|
||||||
|
Works, if and wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and do not modify the
|
||||||
|
License. You may add Your own attribution notices within Derivative Works
|
||||||
|
that You distribute, alongside or as an addendum to the NOTICE text from the
|
||||||
|
Work, provided that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and may provide
|
||||||
|
additional or different license terms and conditions for use, reproduction,
|
||||||
|
or distribution of Your modifications, or for any such Derivative Works as
|
||||||
|
a whole, provided Your use, reproduction, and distribution of the Work otherwise
|
||||||
|
complies with the conditions stated in that License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
||||||
|
Contribution intentionally submitted for inclusion in the Work by You to the
|
||||||
|
Licensor shall be under the terms and conditions of that License, without
|
||||||
|
any additional terms or conditions. Notwithstanding the above, nothing herein
|
||||||
|
shall supersede or modify the terms of any separate license agreement you
|
||||||
|
may have executed with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. that License does not grant permission to use the trade names,
|
||||||
|
trademarks, service marks, or product names of the Licensor, except as required
|
||||||
|
for reasonable and customary use in describing the origin of the Work and
|
||||||
|
reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
|
||||||
|
in writing, Licensor provides the Work (and each Contributor provides its
|
||||||
|
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied, including, without limitation, any warranties
|
||||||
|
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
|
||||||
|
of using or redistributing the Work and assume any risks associated with Your
|
||||||
|
exercise of permissions under that License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory, whether
|
||||||
|
in tort (including negligence), contract, or otherwise, unless required by
|
||||||
|
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
||||||
|
in writing, shall any Contributor be liable to You for damages, including
|
||||||
|
any direct, indirect, special, incidental, or consequential damages of any
|
||||||
|
character arising as a result of that License or out of the use or inability
|
||||||
|
to use the Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all other commercial
|
||||||
|
damages or losses), even if such Contributor has been advised of the possibility
|
||||||
|
of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
||||||
|
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||||
|
acceptance of support, warranty, indemnity, or other liability obligations
|
||||||
|
and/or rights consistent with that License. However, in accepting such obligations,
|
||||||
|
You may act only on Your own behalf and on Your sole responsibility, not on
|
||||||
|
behalf of any other Contributor, and only if You agree to indemnify, defend,
|
||||||
|
and hold each Contributor harmless for any liability incurred by, or claims
|
||||||
|
asserted against, such Contributor by reason of your accepting any such warranty
|
||||||
|
or additional liability. END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following boilerplate
|
||||||
|
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
information. (Don't include the brackets!) The text should be enclosed in
|
||||||
|
the appropriate comment syntax for the file format. We also recommend that
|
||||||
|
a file or class name and description of purpose be included on the same "printed
|
||||||
|
page" as the copyright notice for easier identification within third-party
|
||||||
|
archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
you may not use that file except in compliance with the License.
|
||||||
|
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
|
||||||
|
limitations under the License.
|
||||||
6
vendor/code.hoteas.com/golang/hotime/README.md
generated
vendored
Normal file
6
vendor/code.hoteas.com/golang/hotime/README.md
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# hotime
|
||||||
|
golang web服务框架
|
||||||
|
支持数据库db:mysql、sqlite3
|
||||||
|
支持缓存cache:redis,memory,数据库
|
||||||
|
自带工具类,上下文,以及session等功能
|
||||||
|
|
||||||
765
vendor/code.hoteas.com/golang/hotime/application.go
generated
vendored
Normal file
765
vendor/code.hoteas.com/golang/hotime/application.go
generated
vendored
Normal file
@ -0,0 +1,765 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/cache"
|
||||||
|
"code.hoteas.com/golang/hotime/code"
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
. "code.hoteas.com/golang/hotime/db"
|
||||||
|
. "code.hoteas.com/golang/hotime/log"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
MakeCodeRouter map[string]*code.MakeCode
|
||||||
|
MethodRouter
|
||||||
|
Router
|
||||||
|
Error
|
||||||
|
Log *logrus.Logger
|
||||||
|
WebConnectLog *logrus.Logger
|
||||||
|
Port string //端口号
|
||||||
|
TLSPort string //ssl访问端口号
|
||||||
|
connectListener []func(that *Context) bool //所有的访问监听,true按原计划继续使用,false表示有监听器处理
|
||||||
|
connectDbFunc func(err ...*Error) (master, slave *sql.DB)
|
||||||
|
configPath string
|
||||||
|
Config Map
|
||||||
|
Db HoTimeDB
|
||||||
|
*HoTimeCache
|
||||||
|
*http.Server
|
||||||
|
http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Application) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
that.handler(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 启动实例
|
||||||
|
func (that *Application) Run(router Router) {
|
||||||
|
//如果没有设置配置自动生成配置
|
||||||
|
if that.configPath == "" || len(that.Config) == 0 {
|
||||||
|
that.SetConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
//防止手动设置缓存误伤
|
||||||
|
if that.HoTimeCache == nil {
|
||||||
|
that.SetCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
//防止手动设置session误伤
|
||||||
|
//if that.sessionShort == nil && that.sessionLong == nil {
|
||||||
|
// if that.connectDbFunc == nil {
|
||||||
|
// that.SetSession(CacheIns(&CacheMemory{}), nil)
|
||||||
|
// } else {
|
||||||
|
// that.SetSession(CacheIns(&CacheMemory{}), CacheIns(&CacheDb{Db: &that.Db, Time: that.Config.GetInt64("cacheLongTime")}))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
//that.Router = router
|
||||||
|
if that.Router == nil {
|
||||||
|
that.Router = Router{}
|
||||||
|
}
|
||||||
|
for k, v := range router {
|
||||||
|
if that.Router[k] == nil {
|
||||||
|
that.Router[k] = v
|
||||||
|
}
|
||||||
|
//直达接口层复用
|
||||||
|
for k1, v1 := range v {
|
||||||
|
if that.Router[k][k1] == nil {
|
||||||
|
that.Router[k][k1] = v1
|
||||||
|
}
|
||||||
|
|
||||||
|
for k2, v2 := range v1 {
|
||||||
|
|
||||||
|
that.Router[k][k1][k2] = v2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//重新设置MethodRouter//直达路由
|
||||||
|
that.MethodRouter = MethodRouter{}
|
||||||
|
modeRouterStrict := true
|
||||||
|
if that.Config.GetBool("modeRouterStrict") == false {
|
||||||
|
modeRouterStrict = false
|
||||||
|
}
|
||||||
|
if that.Router != nil {
|
||||||
|
for pk, pv := range that.Router {
|
||||||
|
if !modeRouterStrict {
|
||||||
|
pk = strings.ToLower(pk)
|
||||||
|
}
|
||||||
|
if pv != nil {
|
||||||
|
for ck, cv := range pv {
|
||||||
|
if !modeRouterStrict {
|
||||||
|
ck = strings.ToLower(ck)
|
||||||
|
}
|
||||||
|
if cv != nil {
|
||||||
|
for mk, mv := range cv {
|
||||||
|
if !modeRouterStrict {
|
||||||
|
mk = strings.ToLower(mk)
|
||||||
|
}
|
||||||
|
that.MethodRouter["/"+pk+"/"+ck+"/"+mk] = mv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//that.Port = port
|
||||||
|
that.Port = that.Config.GetString("port")
|
||||||
|
that.TLSPort = that.Config.GetString("tlsPort")
|
||||||
|
|
||||||
|
if that.connectDbFunc != nil && (that.Db.DB == nil || that.Db.DB.Ping() != nil) {
|
||||||
|
that.Db.SetConnect(that.connectDbFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
//异常处理
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
//that.SetError(errors.New(fmt.Sprint(err)), LOG_FMT)
|
||||||
|
that.Log.Warn(err)
|
||||||
|
|
||||||
|
that.Run(router)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
that.Server = &http.Server{}
|
||||||
|
if !IsRun {
|
||||||
|
IsRun = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan int)
|
||||||
|
if ObjToCeilInt(that.Port) != 0 {
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
App[that.Port] = that
|
||||||
|
that.Server.Handler = that
|
||||||
|
//启动服务
|
||||||
|
that.Server.Addr = ":" + that.Port
|
||||||
|
err := that.Server.ListenAndServe()
|
||||||
|
that.Log.Error(err)
|
||||||
|
ch <- 1
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ObjToCeilInt(that.TLSPort) != 0 {
|
||||||
|
go func() {
|
||||||
|
|
||||||
|
App[that.TLSPort] = that
|
||||||
|
that.Server.Handler = that
|
||||||
|
//启动服务
|
||||||
|
that.Server.Addr = ":" + that.TLSPort
|
||||||
|
err := that.Server.ListenAndServeTLS(that.Config.GetString("tlsCert"), that.Config.GetString("tlsKey"))
|
||||||
|
that.Log.Error(err)
|
||||||
|
ch <- 2
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if ObjToCeilInt(that.Port) == 0 && ObjToCeilInt(that.TLSPort) == 0 {
|
||||||
|
that.Log.Error("没有端口启用")
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
value := <-ch
|
||||||
|
|
||||||
|
that.Log.Error("启动服务失败 : " + ObjToStr(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConnectDB 启动实例
|
||||||
|
func (that *Application) SetConnectDB(connect func(err ...*Error) (master, slave *sql.DB)) {
|
||||||
|
|
||||||
|
that.connectDbFunc = connect
|
||||||
|
|
||||||
|
that.Db.SetConnect(that.connectDbFunc, &that.Error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefault 默认配置缓存和session实现
|
||||||
|
func (that *Application) SetDefault(connect func(err ...*Error) (*sql.DB, *sql.DB)) {
|
||||||
|
that.SetConfig()
|
||||||
|
|
||||||
|
if connect != nil {
|
||||||
|
that.connectDbFunc = connect
|
||||||
|
that.Db.SetConnect(that.connectDbFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCache 设置配置文件路径全路径或者相对路径
|
||||||
|
func (that *Application) SetCache() {
|
||||||
|
cacheIns := HoTimeCache{}
|
||||||
|
cacheIns.Init(that.Config.GetMap("cache"), HoTimeDBInterface(&that.Db), &that.Error)
|
||||||
|
that.HoTimeCache = &cacheIns
|
||||||
|
//mode生产模式开启的时候才开启数据库缓存,防止调试出问题
|
||||||
|
if that.Config.GetInt("mode") == 0 {
|
||||||
|
that.Db.HoTimeCache = &cacheIns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfig 设置配置文件路径全路径或者相对路径
|
||||||
|
func (that *Application) SetConfig(configPath ...string) {
|
||||||
|
|
||||||
|
that.Log = GetLog("", true)
|
||||||
|
that.Error = Error{Logger: that.Log}
|
||||||
|
if len(configPath) != 0 {
|
||||||
|
that.configPath = configPath[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.configPath == "" {
|
||||||
|
that.configPath = "config/config.json"
|
||||||
|
}
|
||||||
|
//加载配置文件
|
||||||
|
btes, err := ioutil.ReadFile(that.configPath)
|
||||||
|
that.Config = DeepCopyMap(Config).(Map)
|
||||||
|
if err == nil {
|
||||||
|
|
||||||
|
cmap := Map{}
|
||||||
|
//文件是否损坏
|
||||||
|
cmap.JsonToMap(string(btes), &that.Error)
|
||||||
|
|
||||||
|
for k, v := range cmap {
|
||||||
|
that.Config[k] = v //程序配置
|
||||||
|
Config[k] = v //系统配置
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
that.Log.Error("配置文件不存在,或者配置出错,使用缺省默认配置")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.Error.GetError() != nil {
|
||||||
|
fmt.Println(that.Error.GetError().Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
//文件如果损坏则不写入配置防止配置文件数据丢失
|
||||||
|
if that.Error.GetError() == nil {
|
||||||
|
//var configByte bytes.Buffer
|
||||||
|
|
||||||
|
//判断配置文件是否序列有变化,有则修改配置,无则不变
|
||||||
|
//fmt.Println(len(btes))
|
||||||
|
configStr := that.Config.ToJsonString()
|
||||||
|
if len(btes) != 0 && configStr == string(btes) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//写入配置说明
|
||||||
|
//var configNoteByte bytes.Buffer
|
||||||
|
configNoteStr := ConfigNote.ToJsonString()
|
||||||
|
//_ = json.Indent(&configNoteByte, []byte(ConfigNote.ToJsonString()), "", "\t")
|
||||||
|
|
||||||
|
_ = os.MkdirAll(filepath.Dir(that.configPath), os.ModeDir)
|
||||||
|
err = ioutil.WriteFile(that.configPath, []byte(configStr), os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
that.Error.SetError(err)
|
||||||
|
}
|
||||||
|
_ = ioutil.WriteFile(filepath.Dir(that.configPath)+"/configNote.json", []byte(configNoteStr), os.ModePerm)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Log = GetLog(that.Config.GetString("logFile"), true)
|
||||||
|
that.Error = Error{Logger: that.Log}
|
||||||
|
if that.Config.Get("webConnectLogShow") == nil || that.Config.GetBool("webConnectLogShow") {
|
||||||
|
that.WebConnectLog = GetLog(that.Config.GetString("webConnectLogFile"), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConnectListener 连接判断,返回false继续传输至控制层,true则停止传输
|
||||||
|
func (that *Application) SetConnectListener(lis func(that *Context) (isFinished bool)) {
|
||||||
|
that.connectListener = append(that.connectListener, lis)
|
||||||
|
}
|
||||||
|
|
||||||
|
//网络错误
|
||||||
|
//func (that *Application) session(w http.ResponseWriter, req *http.Request) {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
//序列化链接
|
||||||
|
func (that *Application) urlSer(url string) (string, []string) {
|
||||||
|
q := strings.Index(url, "?")
|
||||||
|
if q == -1 {
|
||||||
|
q = len(url)
|
||||||
|
}
|
||||||
|
o := Substr(url, 0, q)
|
||||||
|
|
||||||
|
r := strings.SplitN(o, "/", -1)
|
||||||
|
|
||||||
|
var s = make([]string, 0)
|
||||||
|
|
||||||
|
for i := 0; i < len(r); i++ {
|
||||||
|
if !strings.EqualFold("", r[i]) {
|
||||||
|
s = append(s, r[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o, s
|
||||||
|
}
|
||||||
|
|
||||||
|
//访问
|
||||||
|
|
||||||
|
func (that *Application) handler(w http.ResponseWriter, req *http.Request) {
|
||||||
|
nowUnixTime := time.Now()
|
||||||
|
|
||||||
|
_, s := that.urlSer(req.RequestURI)
|
||||||
|
//获取cookie
|
||||||
|
// 如果cookie存在直接将sessionId赋值为cookie.Value
|
||||||
|
// 如果cookie不存在就查找传入的参数中是否有token
|
||||||
|
// 如果token不存在就生成随机的sessionId
|
||||||
|
// 如果token存在就判断token是否在Session中有保存
|
||||||
|
// 如果有取出token并复制给cookie
|
||||||
|
// 没有保存就生成随机的session
|
||||||
|
cookie, err := req.Cookie(that.Config.GetString("sessionName"))
|
||||||
|
sessionId := Md5(strconv.Itoa(Rand(10)))
|
||||||
|
needSetCookie := ""
|
||||||
|
token := req.Header.Get("Authorization")
|
||||||
|
if len(token) != 32 {
|
||||||
|
|
||||||
|
token = req.FormValue("token")
|
||||||
|
}
|
||||||
|
//没有cookie或者cookie不等于token
|
||||||
|
//有token优先token
|
||||||
|
if len(token) == 32 {
|
||||||
|
sessionId = token
|
||||||
|
//没有token,则查阅session
|
||||||
|
} else if err == nil && cookie.Value != "" {
|
||||||
|
sessionId = cookie.Value
|
||||||
|
//session也没有则判断是否创建cookie
|
||||||
|
} else {
|
||||||
|
needSetCookie = sessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
unescapeUrl, err := url.QueryUnescape(req.RequestURI)
|
||||||
|
if err != nil {
|
||||||
|
unescapeUrl = req.RequestURI
|
||||||
|
}
|
||||||
|
//访问实例
|
||||||
|
context := Context{SessionIns: SessionIns{SessionId: sessionId, HoTimeCache: that.HoTimeCache},
|
||||||
|
Resp: w, Req: req, Application: that, RouterString: s, Config: that.Config, Db: &that.Db,
|
||||||
|
HandlerStr: unescapeUrl}
|
||||||
|
//header默认设置
|
||||||
|
header := w.Header()
|
||||||
|
header.Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
|
||||||
|
//url去掉参数并序列化
|
||||||
|
context.HandlerStr, context.RouterString = that.urlSer(context.HandlerStr)
|
||||||
|
|
||||||
|
//跨域设置
|
||||||
|
that.crossDomain(&context, needSetCookie)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
//是否展示日志
|
||||||
|
if that.WebConnectLog != nil {
|
||||||
|
|
||||||
|
//负载均衡优化
|
||||||
|
ipStr := ""
|
||||||
|
if req.Header.Get("X-Forwarded-For") != "" {
|
||||||
|
ipStr = req.Header.Get("X-Forwarded-For")
|
||||||
|
} else if req.Header.Get("X-Real-IP") != "" {
|
||||||
|
ipStr = req.Header.Get("X-Real-IP")
|
||||||
|
}
|
||||||
|
//负载均衡优化
|
||||||
|
if ipStr == "" {
|
||||||
|
//RemoteAddr := that.Req.RemoteAddr
|
||||||
|
ipStr = Substr(context.Req.RemoteAddr, 0, strings.Index(context.Req.RemoteAddr, ":"))
|
||||||
|
}
|
||||||
|
|
||||||
|
that.WebConnectLog.Infoln(ipStr, context.Req.Method,
|
||||||
|
"time cost:", ObjToFloat64(time.Now().UnixNano()-nowUnixTime.UnixNano())/1000000.00, "ms",
|
||||||
|
"data length:", ObjToFloat64(context.DataSize)/1000.00, "KB", context.HandlerStr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//访问拦截true继续false暂停
|
||||||
|
connectListenerLen := len(that.connectListener) - 1
|
||||||
|
|
||||||
|
for true {
|
||||||
|
if connectListenerLen < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if that.connectListener[connectListenerLen](&context) {
|
||||||
|
context.View()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
connectListenerLen--
|
||||||
|
}
|
||||||
|
|
||||||
|
//接口服务
|
||||||
|
//验证接口严格模式
|
||||||
|
modeRouterStrict := that.Config.GetBool("modeRouterStrict")
|
||||||
|
tempHandlerStr := context.HandlerStr
|
||||||
|
if !modeRouterStrict {
|
||||||
|
tempHandlerStr = strings.ToLower(tempHandlerStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//执行接口
|
||||||
|
if that.MethodRouter[tempHandlerStr] != nil {
|
||||||
|
that.MethodRouter[tempHandlerStr](&context)
|
||||||
|
context.View()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//url赋值
|
||||||
|
path := that.Config.GetString("tpt") + tempHandlerStr
|
||||||
|
|
||||||
|
//判断是否为默认
|
||||||
|
if path[len(path)-1] == '/' {
|
||||||
|
defFile := that.Config.GetSlice("defFile")
|
||||||
|
|
||||||
|
for i := 0; i < len(defFile); i++ {
|
||||||
|
temp := path + defFile.GetString(i)
|
||||||
|
_, err := os.Stat(temp)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
path = temp
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if path[len(path)-1] == '/' {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(path, "/.") {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置header
|
||||||
|
delete(header, "Content-Type")
|
||||||
|
if that.Config.GetInt("mode") == 0 {
|
||||||
|
header.Set("Cache-Control", "public")
|
||||||
|
} else {
|
||||||
|
header.Set("Cache-Control", "no-cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := strings.LastIndex(path, ".")
|
||||||
|
if t != -1 {
|
||||||
|
tt := path[t:]
|
||||||
|
|
||||||
|
if MimeMaps[tt] != "" {
|
||||||
|
|
||||||
|
header.Add("Content-Type", MimeMaps[tt])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//w.Write(data)
|
||||||
|
http.ServeFile(w, req, path)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Application) crossDomain(context *Context, sessionId string) {
|
||||||
|
|
||||||
|
//没有跨域设置
|
||||||
|
if context.Config.GetString("crossDomain") == "" {
|
||||||
|
if sessionId != "" {
|
||||||
|
http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
header := context.Resp.Header()
|
||||||
|
|
||||||
|
//不跨域,则不设置
|
||||||
|
remoteHost := context.Req.Host
|
||||||
|
if context.Config.GetString("port") == "80" || context.Config.GetString("port") == "443" {
|
||||||
|
remoteHost = remoteHost + ":" + context.Config.GetString("port")
|
||||||
|
}
|
||||||
|
if context.Config.GetString("crossDomain") != "auto" {
|
||||||
|
//不跨域,则不设置
|
||||||
|
if strings.Contains(context.Config.GetString("crossDomain"), remoteHost) {
|
||||||
|
|
||||||
|
if sessionId != "" {
|
||||||
|
http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.Set("Access-Control-Allow-Origin", that.Config.GetString("crossDomain"))
|
||||||
|
// 后端设置,2592000单位秒,这里是30天
|
||||||
|
header.Set("Access-Control-Max-Age", "2592000")
|
||||||
|
|
||||||
|
//header.Set("Access-Control-Allow-Origin", "*")
|
||||||
|
header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE")
|
||||||
|
header.Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
header.Set("Access-Control-Expose-Headers", "*")
|
||||||
|
header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token")
|
||||||
|
|
||||||
|
if sessionId != "" {
|
||||||
|
//跨域允许需要设置cookie的允许跨域https才有效果
|
||||||
|
context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
origin := context.Req.Header.Get("Origin")
|
||||||
|
|
||||||
|
refer := context.Req.Header.Get("Referer")
|
||||||
|
if (origin != "" && strings.Contains(origin, remoteHost)) || strings.Contains(refer, remoteHost) {
|
||||||
|
|
||||||
|
if sessionId != "" {
|
||||||
|
http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if origin != "" {
|
||||||
|
header.Set("Access-Control-Allow-Origin", origin)
|
||||||
|
//return
|
||||||
|
} else if refer != "" {
|
||||||
|
tempInt := 0
|
||||||
|
lastInt := strings.IndexFunc(refer, func(r rune) bool {
|
||||||
|
if r == '/' && tempInt > 8 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
tempInt++
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if lastInt < 0 {
|
||||||
|
lastInt = len(refer)
|
||||||
|
}
|
||||||
|
refer = Substr(refer, 0, lastInt)
|
||||||
|
header.Set("Access-Control-Allow-Origin", refer)
|
||||||
|
//header.Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE")
|
||||||
|
header.Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
header.Set("Access-Control-Expose-Headers", "*")
|
||||||
|
header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token")
|
||||||
|
|
||||||
|
if sessionId != "" {
|
||||||
|
//跨域允许需要设置cookie的允许跨域https才有效果
|
||||||
|
context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Init 初始化application
|
||||||
|
func Init(config string) *Application {
|
||||||
|
appIns := Application{}
|
||||||
|
//手动模式,
|
||||||
|
appIns.SetConfig(config)
|
||||||
|
|
||||||
|
SetDB(&appIns)
|
||||||
|
appIns.SetCache()
|
||||||
|
codeConfig := appIns.Config.GetSlice("codeConfig")
|
||||||
|
|
||||||
|
if codeConfig != nil {
|
||||||
|
|
||||||
|
for k, _ := range codeConfig {
|
||||||
|
codeMake := codeConfig.GetMap(k)
|
||||||
|
if codeMake == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//codeMake["table"] = k
|
||||||
|
if appIns.MakeCodeRouter == nil {
|
||||||
|
appIns.MakeCodeRouter = map[string]*code.MakeCode{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if codeMake.GetString("name") == "" {
|
||||||
|
codeMake["name"] = codeMake.GetString("table")
|
||||||
|
}
|
||||||
|
|
||||||
|
appIns.MakeCodeRouter[codeMake.GetString("name")] = &code.MakeCode{Error: appIns.Error}
|
||||||
|
appIns.MakeCodeRouter[codeMake.GetString("name")].Db2JSON(&appIns.Db, codeMake)
|
||||||
|
|
||||||
|
//接入动态代码层
|
||||||
|
if appIns.Router == nil {
|
||||||
|
appIns.Router = Router{}
|
||||||
|
}
|
||||||
|
|
||||||
|
//appIns.Router[codeMake.GetString("name")] = TptProject
|
||||||
|
appIns.Router[codeMake.GetString("name")] = Proj{}
|
||||||
|
for k2, _ := range TptProject {
|
||||||
|
appIns.Router[codeMake.GetString("name")][k2] = Ctr{}
|
||||||
|
for k3, v3 := range TptProject[k2] {
|
||||||
|
appIns.Router[codeMake.GetString("name")][k2][k3] = v3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k1, _ := range appIns.MakeCodeRouter[codeMake.GetString("name")].TableColumns {
|
||||||
|
appIns.Router[codeMake.GetString("name")][k1] = appIns.Router[codeMake.GetString("name")]["hotimeCommon"]
|
||||||
|
}
|
||||||
|
|
||||||
|
setMakeCodeListener(codeMake.GetString("name"), &appIns)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return &appIns
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDB 智能数据库设置
|
||||||
|
func SetDB(appIns *Application) {
|
||||||
|
db := appIns.Config.GetMap("db")
|
||||||
|
dbSqlite := db.GetMap("sqlite")
|
||||||
|
dbMysql := db.GetMap("mysql")
|
||||||
|
if db != nil && dbSqlite != nil {
|
||||||
|
SetSqliteDB(appIns, dbSqlite)
|
||||||
|
}
|
||||||
|
if db != nil && dbMysql != nil {
|
||||||
|
SetMysqlDB(appIns, dbMysql)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func SetMysqlDB(appIns *Application, config Map) {
|
||||||
|
|
||||||
|
appIns.Db.Type = "mysql"
|
||||||
|
appIns.Db.DBName = config.GetString("name")
|
||||||
|
appIns.Db.Prefix = config.GetString("prefix")
|
||||||
|
appIns.Db.Log = appIns.Log
|
||||||
|
appIns.Db.Mode = appIns.Config.GetCeilInt("mode")
|
||||||
|
appIns.SetConnectDB(func(err ...*Error) (master, slave *sql.DB) {
|
||||||
|
//master数据库配置
|
||||||
|
query := config.GetString("user") + ":" + config.GetString("password") +
|
||||||
|
"@tcp(" + config.GetString("host") + ":" + config.GetString("port") + ")/" + config.GetString("name") + "?charset=utf8"
|
||||||
|
DB, e := sql.Open("mysql", query)
|
||||||
|
if e != nil && len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
}
|
||||||
|
master = DB
|
||||||
|
//slave数据库配置
|
||||||
|
configSlave := config.GetMap("slave")
|
||||||
|
if configSlave != nil {
|
||||||
|
query := configSlave.GetString("user") + ":" + configSlave.GetString("password") +
|
||||||
|
"@tcp(" + config.GetString("host") + ":" + configSlave.GetString("port") + ")/" + configSlave.GetString("name") + "?charset=utf8"
|
||||||
|
DB1, e := sql.Open("mysql", query)
|
||||||
|
if e != nil && len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
}
|
||||||
|
slave = DB1
|
||||||
|
}
|
||||||
|
|
||||||
|
return master, slave
|
||||||
|
//return DB
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func SetSqliteDB(appIns *Application, config Map) {
|
||||||
|
|
||||||
|
appIns.Db.Type = "sqlite"
|
||||||
|
appIns.Db.Prefix = config.GetString("prefix")
|
||||||
|
appIns.Db.Mode = appIns.Config.GetCeilInt("mode")
|
||||||
|
appIns.Db.Log = appIns.Log
|
||||||
|
appIns.SetConnectDB(func(err ...*Error) (master, slave *sql.DB) {
|
||||||
|
db, e := sql.Open("sqlite3", config.GetString("path"))
|
||||||
|
if e != nil && len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
}
|
||||||
|
master = db
|
||||||
|
|
||||||
|
return master, slave
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func setMakeCodeListener(name string, appIns *Application) {
|
||||||
|
appIns.SetConnectListener(func(context *Context) (isFinished bool) {
|
||||||
|
|
||||||
|
codeIns := appIns.MakeCodeRouter[name]
|
||||||
|
|
||||||
|
if len(context.RouterString) < 2 || appIns.MakeCodeRouter[context.RouterString[0]] == nil {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(context.RouterString) > 1 && context.RouterString[0] == name {
|
||||||
|
if context.RouterString[1] == "hotime" && context.RouterString[2] == "login" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
if context.RouterString[1] == "hotime" && context.RouterString[2] == "logout" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
if context.RouterString[1] == "hotime" && context.RouterString[2] == "config" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
if context.RouterString[1] == "hotime" && context.RouterString[2] == "wallpaper" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
if context.Session(codeIns.FileConfig.GetString("table")+"_id").Data == nil {
|
||||||
|
context.Display(2, "你还没有登录")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(context.RouterString) < 2 || len(context.RouterString) > 3 ||
|
||||||
|
!(context.Router[context.RouterString[0]] != nil &&
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]] != nil) {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
//排除无效操作
|
||||||
|
if len(context.RouterString) == 2 &&
|
||||||
|
context.Req.Method != "GET" &&
|
||||||
|
context.Req.Method != "POST" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
//排除已有接口的无效操作
|
||||||
|
if len(context.RouterString) == 3 && context.Router[context.RouterString[0]] != nil && context.Router[context.RouterString[0]][context.RouterString[1]] != nil && context.Router[context.RouterString[0]][context.RouterString[1]][context.RouterString[2]] != nil {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
//列表检索
|
||||||
|
if len(context.RouterString) == 2 &&
|
||||||
|
context.Req.Method == "GET" {
|
||||||
|
if context.Router[context.RouterString[0]][context.RouterString[1]]["search"] == nil {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]]["search"](context)
|
||||||
|
}
|
||||||
|
//新建
|
||||||
|
if len(context.RouterString) == 2 &&
|
||||||
|
context.Req.Method == "POST" {
|
||||||
|
if context.Router[context.RouterString[0]][context.RouterString[1]]["add"] == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]]["add"](context)
|
||||||
|
}
|
||||||
|
if len(context.RouterString) == 3 &&
|
||||||
|
context.Req.Method == "POST" {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
//查询单条
|
||||||
|
if len(context.RouterString) == 3 &&
|
||||||
|
context.Req.Method == "GET" {
|
||||||
|
|
||||||
|
if context.Router[context.RouterString[0]][context.RouterString[1]]["info"] == nil {
|
||||||
|
return isFinished
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]]["info"](context)
|
||||||
|
}
|
||||||
|
//更新
|
||||||
|
if len(context.RouterString) == 3 &&
|
||||||
|
context.Req.Method == "PUT" {
|
||||||
|
|
||||||
|
if context.Router[context.RouterString[0]][context.RouterString[1]]["update"] == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]]["update"](context)
|
||||||
|
}
|
||||||
|
//移除
|
||||||
|
if len(context.RouterString) == 3 &&
|
||||||
|
context.Req.Method == "DELETE" {
|
||||||
|
|
||||||
|
if context.Router[context.RouterString[0]][context.RouterString[1]]["remove"] == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Router[context.RouterString[0]][context.RouterString[1]]["remove"](context)
|
||||||
|
}
|
||||||
|
|
||||||
|
//context.View()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
277
vendor/code.hoteas.com/golang/hotime/cache/cache.go
generated
vendored
Normal file
277
vendor/code.hoteas.com/golang/hotime/cache/cache.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HoTimeCache 可配置memory,db,redis,默认启用memory,默认优先级为memory>redis>db,memory与数据库缓存设置项一致,
|
||||||
|
//缓存数据填充会自动反方向反哺,加入memory缓存过期将自动从redis更新,但memory永远不会更新redis,如果是集群建议不要开启memory,配置即启用
|
||||||
|
type HoTimeCache struct {
|
||||||
|
*Error
|
||||||
|
dbCache *CacheDb
|
||||||
|
redisCache *CacheRedis
|
||||||
|
memoryCache *CacheMemory
|
||||||
|
Config Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *HoTimeCache) Session(key string, data ...interface{}) *Obj {
|
||||||
|
var reData *Obj
|
||||||
|
if len(data) == 0 {
|
||||||
|
//内存缓存有
|
||||||
|
if that.memoryCache != nil && that.memoryCache.SessionSet {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil && that.redisCache.SessionSet {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
if that.memoryCache != nil && that.memoryCache.SessionSet {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//db缓存有
|
||||||
|
if that.dbCache != nil && that.dbCache.SessionSet {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
if that.memoryCache != nil && that.memoryCache.SessionSet {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
if that.redisCache != nil && that.redisCache.SessionSet {
|
||||||
|
that.redisCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置缓存
|
||||||
|
//设置内存缓存
|
||||||
|
if that.memoryCache != nil && that.memoryCache.SessionSet {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil && that.redisCache.SessionSet {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.dbCache != nil && that.dbCache.SessionSet {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
|
||||||
|
}
|
||||||
|
func (that *HoTimeCache) Db(key string, data ...interface{}) *Obj {
|
||||||
|
var reData *Obj
|
||||||
|
if len(data) == 0 {
|
||||||
|
//内存缓存有
|
||||||
|
if that.memoryCache != nil && that.memoryCache.DbSet {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil && that.redisCache.DbSet {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
if that.memoryCache != nil && that.memoryCache.DbSet {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//redis缓存有
|
||||||
|
if that.dbCache != nil && that.dbCache.DbSet {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
if that.memoryCache != nil && that.memoryCache.DbSet {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.redisCache != nil && that.redisCache.DbSet {
|
||||||
|
that.redisCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置缓存
|
||||||
|
//设置内存缓存
|
||||||
|
if that.memoryCache != nil && that.memoryCache.DbSet {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil && that.redisCache.DbSet {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.dbCache != nil && that.dbCache.DbSet {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *HoTimeCache) Cache(key string, data ...interface{}) *Obj {
|
||||||
|
var reData *Obj
|
||||||
|
if len(data) == 0 {
|
||||||
|
//内存缓存有
|
||||||
|
if that.memoryCache != nil {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
if reData != nil {
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
|
||||||
|
if that.memoryCache != nil {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//redis缓存有
|
||||||
|
if that.dbCache != nil {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
if reData.Data != nil {
|
||||||
|
if that.memoryCache != nil {
|
||||||
|
that.memoryCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
if that.redisCache != nil {
|
||||||
|
that.redisCache.Cache(key, reData.Data)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
//设置缓存
|
||||||
|
//设置内存缓存
|
||||||
|
if that.memoryCache != nil {
|
||||||
|
reData = that.memoryCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.redisCache != nil {
|
||||||
|
reData = that.redisCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
//redis缓存有
|
||||||
|
if that.dbCache != nil {
|
||||||
|
reData = that.dbCache.Cache(key, data...)
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *HoTimeCache) Init(config Map, hotimeDb HoTimeDBInterface, err ...*Error) {
|
||||||
|
//防止空数据问题
|
||||||
|
if config == nil {
|
||||||
|
config = Map{}
|
||||||
|
}
|
||||||
|
if err[0] != nil {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
that.Config = config
|
||||||
|
//memory配置初始化
|
||||||
|
memory := that.Config.GetMap("memory")
|
||||||
|
if memory == nil {
|
||||||
|
memory = Map{
|
||||||
|
"db": true,
|
||||||
|
"session": true,
|
||||||
|
"sort": 0,
|
||||||
|
"timeout": 60 * 60 * 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if memory.Get("db") == nil {
|
||||||
|
memory["db"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory.Get("session") == nil {
|
||||||
|
memory["session"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if memory.Get("timeout") == nil {
|
||||||
|
memory["timeout"] = 60 * 60 * 2
|
||||||
|
}
|
||||||
|
that.Config["memory"] = memory
|
||||||
|
that.memoryCache = &CacheMemory{TimeOut: memory.GetCeilInt64("timeout"),
|
||||||
|
DbSet: memory.GetBool("db"), SessionSet: memory.GetBool("session")}
|
||||||
|
if err[0] != nil {
|
||||||
|
that.memoryCache.SetError(err[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
//db配置初始化
|
||||||
|
redis := that.Config.GetMap("redis")
|
||||||
|
if redis != nil {
|
||||||
|
if redis.GetString("host") == "" || redis.GetString("port") == "" {
|
||||||
|
if err[0] != nil {
|
||||||
|
err[0].SetError(errors.New("请检查redis配置host和port配置"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if redis.Get("db") == nil {
|
||||||
|
redis["db"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if redis.Get("session") == nil {
|
||||||
|
redis["session"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if redis.Get("timeout") == nil {
|
||||||
|
redis["timeout"] = 60 * 60 * 24 * 15
|
||||||
|
}
|
||||||
|
that.Config["redis"] = redis
|
||||||
|
that.redisCache = &CacheRedis{TimeOut: redis.GetCeilInt64("timeout"),
|
||||||
|
DbSet: redis.GetBool("db"), SessionSet: redis.GetBool("session"), Host: redis.GetString("host"),
|
||||||
|
Pwd: redis.GetString("password"), Port: redis.GetCeilInt64("port")}
|
||||||
|
|
||||||
|
if err[0] != nil {
|
||||||
|
that.redisCache.SetError(err[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//db配置初始化
|
||||||
|
db := that.Config.GetMap("db")
|
||||||
|
if db != nil {
|
||||||
|
|
||||||
|
if db.Get("db") == nil {
|
||||||
|
db["db"] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Get("session") == nil {
|
||||||
|
db["session"] = true
|
||||||
|
}
|
||||||
|
if db.Get("timeout") == nil {
|
||||||
|
db["timeout"] = 60 * 60 * 24 * 30
|
||||||
|
}
|
||||||
|
that.Config["db"] = db
|
||||||
|
|
||||||
|
that.dbCache = &CacheDb{TimeOut: db.GetCeilInt64("timeout"),
|
||||||
|
DbSet: db.GetBool("db"), SessionSet: db.GetBool("session"),
|
||||||
|
Db: hotimeDb}
|
||||||
|
|
||||||
|
if err[0] != nil {
|
||||||
|
that.dbCache.SetError(err[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
173
vendor/code.hoteas.com/golang/hotime/cache/cache_db.go
generated
vendored
Normal file
173
vendor/code.hoteas.com/golang/hotime/cache/cache_db.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HoTimeDBInterface interface {
|
||||||
|
GetPrefix() string
|
||||||
|
Query(query string, args ...interface{}) []Map
|
||||||
|
Exec(query string, args ...interface{}) (sql.Result, *Error)
|
||||||
|
Get(table string, qu ...interface{}) Map
|
||||||
|
Select(table string, qu ...interface{}) []Map
|
||||||
|
Delete(table string, data map[string]interface{}) int64
|
||||||
|
Update(table string, data Map, where Map) int64
|
||||||
|
Insert(table string, data map[string]interface{}) int64
|
||||||
|
GetType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheDb struct {
|
||||||
|
TimeOut int64
|
||||||
|
DbSet bool
|
||||||
|
SessionSet bool
|
||||||
|
Db HoTimeDBInterface
|
||||||
|
*Error
|
||||||
|
ContextBase
|
||||||
|
isInit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheDb) GetError() *Error {
|
||||||
|
|
||||||
|
return that.Error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheDb) SetError(err *Error) {
|
||||||
|
that.Error = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheDb) initDbTable() {
|
||||||
|
if that.isInit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if that.Db.GetType() == "mysql" {
|
||||||
|
|
||||||
|
dbNames := that.Db.Query("SELECT DATABASE()")
|
||||||
|
|
||||||
|
if len(dbNames) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dbName := dbNames[0].GetString("DATABASE()")
|
||||||
|
res := that.Db.Query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='" + dbName + "' AND TABLE_NAME='" + that.Db.GetPrefix() + "cached'")
|
||||||
|
if len(res) != 0 {
|
||||||
|
that.isInit = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, e := that.Db.Exec("CREATE TABLE `" + that.Db.GetPrefix() + "cached` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(60) DEFAULT NULL, `value` varchar(2000) DEFAULT NULL, `time` bigint(20) DEFAULT NULL, `endtime` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198740 DEFAULT CHARSET=utf8")
|
||||||
|
if e.GetError() == nil {
|
||||||
|
that.isInit = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.Db.GetType() == "sqlite" {
|
||||||
|
res := that.Db.Query(`select * from sqlite_master where type = 'table' and name = '` + that.Db.GetPrefix() + `cached'`)
|
||||||
|
|
||||||
|
if len(res) != 0 {
|
||||||
|
that.isInit = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, e := that.Db.Exec(`CREATE TABLE "` + that.Db.GetPrefix() + `cached" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"key" TEXT(60),
|
||||||
|
"value" TEXT(2000),
|
||||||
|
"time" integer,
|
||||||
|
"endtime" integer
|
||||||
|
);`)
|
||||||
|
if e.GetError() == nil {
|
||||||
|
that.isInit = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取Cache键只能为string类型
|
||||||
|
func (that *CacheDb) get(key string) interface{} {
|
||||||
|
|
||||||
|
cached := that.Db.Get("cached", "*", Map{"key": key})
|
||||||
|
|
||||||
|
if cached == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
//data:=cacheMap[key];
|
||||||
|
if cached.GetInt64("endtime") <= time.Now().Unix() {
|
||||||
|
|
||||||
|
that.Db.Delete("cached", Map{"id": cached.GetString("id")})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data := Map{}
|
||||||
|
data.JsonToMap(cached.GetString("value"))
|
||||||
|
|
||||||
|
return data.Get("data")
|
||||||
|
}
|
||||||
|
|
||||||
|
//key value ,时间为时间戳
|
||||||
|
func (that *CacheDb) set(key string, value interface{}, tim int64) {
|
||||||
|
|
||||||
|
bte, _ := json.Marshal(Map{"data": value})
|
||||||
|
|
||||||
|
num := that.Db.Update("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim}, Map{"key": key})
|
||||||
|
if num == int64(0) {
|
||||||
|
that.Db.Insert("cached", Map{"value": string(bte), "time": time.Now().UnixNano(), "endtime": tim, "key": key})
|
||||||
|
}
|
||||||
|
|
||||||
|
//随机执行删除命令
|
||||||
|
if Rand(1000) > 950 {
|
||||||
|
that.Db.Delete("cached", Map{"endtime[<]": time.Now().Unix()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheDb) delete(key string) {
|
||||||
|
|
||||||
|
del := strings.Index(key, "*")
|
||||||
|
//如果通配删除
|
||||||
|
if del != -1 {
|
||||||
|
key = Substr(key, 0, del)
|
||||||
|
that.Db.Delete("cached", Map{"key": key + "%"})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
that.Db.Delete("cached", Map{"key": key})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheDb) Cache(key string, data ...interface{}) *Obj {
|
||||||
|
|
||||||
|
that.initDbTable()
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
return &Obj{Data: that.get(key)}
|
||||||
|
}
|
||||||
|
tim := time.Now().Unix()
|
||||||
|
|
||||||
|
if len(data) == 1 && data[0] == nil {
|
||||||
|
that.delete(key)
|
||||||
|
return &Obj{Data: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == 1 {
|
||||||
|
if that.TimeOut == 0 {
|
||||||
|
//that.Time = Config.GetInt64("cacheLongTime")
|
||||||
|
}
|
||||||
|
tim += that.TimeOut
|
||||||
|
}
|
||||||
|
if len(data) == 2 {
|
||||||
|
that.SetError(nil)
|
||||||
|
tempt := ObjToInt64(data[1], that.Error)
|
||||||
|
|
||||||
|
if tempt > tim {
|
||||||
|
tim = tempt
|
||||||
|
} else if that.GetError() == nil {
|
||||||
|
|
||||||
|
tim = tim + tempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
that.set(key, data[0], tim)
|
||||||
|
return &Obj{Data: nil}
|
||||||
|
}
|
||||||
158
vendor/code.hoteas.com/golang/hotime/cache/cache_memory.go
generated
vendored
Normal file
158
vendor/code.hoteas.com/golang/hotime/cache/cache_memory.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheMemory struct {
|
||||||
|
TimeOut int64
|
||||||
|
DbSet bool
|
||||||
|
SessionSet bool
|
||||||
|
Map
|
||||||
|
*Error
|
||||||
|
ContextBase
|
||||||
|
mutex *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheMemory) GetError() *Error {
|
||||||
|
|
||||||
|
return that.Error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheMemory) SetError(err *Error) {
|
||||||
|
that.Error = err
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取Cache键只能为string类型
|
||||||
|
func (that *CacheMemory) get(key string) interface{} {
|
||||||
|
that.Error.SetError(nil)
|
||||||
|
if that.Map == nil {
|
||||||
|
that.Map = Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.Map[key] == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := that.Map.Get(key, that.Error).(cacheData)
|
||||||
|
if that.Error.GetError() != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.time < time.Now().Unix() {
|
||||||
|
delete(that.Map, key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheMemory) refreshMap() {
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
that.mutex.Lock()
|
||||||
|
defer that.mutex.Unlock()
|
||||||
|
for key, v := range that.Map {
|
||||||
|
data := v.(cacheData)
|
||||||
|
if data.time <= time.Now().Unix() {
|
||||||
|
delete(that.Map, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//key value ,时间为时间戳
|
||||||
|
func (that *CacheMemory) set(key string, value interface{}, time int64) {
|
||||||
|
that.Error.SetError(nil)
|
||||||
|
var data cacheData
|
||||||
|
|
||||||
|
if that.Map == nil {
|
||||||
|
that.Map = Map{}
|
||||||
|
}
|
||||||
|
|
||||||
|
dd := that.Map[key]
|
||||||
|
|
||||||
|
if dd == nil {
|
||||||
|
data = cacheData{}
|
||||||
|
} else {
|
||||||
|
data = dd.(cacheData)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.time = time
|
||||||
|
data.data = value
|
||||||
|
|
||||||
|
that.Map.Put(key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheMemory) delete(key string) {
|
||||||
|
del := strings.Index(key, "*")
|
||||||
|
//如果通配删除
|
||||||
|
if del != -1 {
|
||||||
|
key = Substr(key, 0, del)
|
||||||
|
for k, _ := range that.Map {
|
||||||
|
if strings.Index(k, key) != -1 {
|
||||||
|
delete(that.Map, k)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
delete(that.Map, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheMemory) Cache(key string, data ...interface{}) *Obj {
|
||||||
|
|
||||||
|
x := RandX(1, 100000)
|
||||||
|
if x > 99950 {
|
||||||
|
that.refreshMap()
|
||||||
|
}
|
||||||
|
if that.mutex == nil {
|
||||||
|
that.mutex = &sync.RWMutex{}
|
||||||
|
}
|
||||||
|
|
||||||
|
reData := &Obj{Data: nil}
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
that.mutex.RLock()
|
||||||
|
reData.Data = that.get(key)
|
||||||
|
that.mutex.RUnlock()
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
tim := time.Now().Unix()
|
||||||
|
|
||||||
|
if len(data) == 1 && data[0] == nil {
|
||||||
|
that.mutex.Lock()
|
||||||
|
that.delete(key)
|
||||||
|
that.mutex.Unlock()
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == 1 {
|
||||||
|
|
||||||
|
tim = tim + that.TimeOut
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(data) == 2 {
|
||||||
|
that.Error.SetError(nil)
|
||||||
|
tempt := ObjToInt64(data[1], that.Error)
|
||||||
|
|
||||||
|
if tempt > tim {
|
||||||
|
|
||||||
|
tim = tempt
|
||||||
|
} else if that.Error.GetError() == nil {
|
||||||
|
|
||||||
|
tim = tim + tempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
that.mutex.Lock()
|
||||||
|
that.set(key, data[0], tim)
|
||||||
|
that.mutex.Unlock()
|
||||||
|
return reData
|
||||||
|
|
||||||
|
}
|
||||||
170
vendor/code.hoteas.com/golang/hotime/cache/cache_redis.go
generated
vendored
Normal file
170
vendor/code.hoteas.com/golang/hotime/cache/cache_redis.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"github.com/garyburd/redigo/redis"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheRedis struct {
|
||||||
|
TimeOut int64
|
||||||
|
DbSet bool
|
||||||
|
SessionSet bool
|
||||||
|
Host string
|
||||||
|
Pwd string
|
||||||
|
Port int64
|
||||||
|
conn redis.Conn
|
||||||
|
tag int64
|
||||||
|
ContextBase
|
||||||
|
*Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheRedis) GetError() *Error {
|
||||||
|
|
||||||
|
return that.Error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheRedis) SetError(err *Error) {
|
||||||
|
that.Error = err
|
||||||
|
}
|
||||||
|
|
||||||
|
//唯一标志
|
||||||
|
func (that *CacheRedis) GetTag() int64 {
|
||||||
|
|
||||||
|
if that.tag == int64(0) {
|
||||||
|
that.tag = time.Now().UnixNano()
|
||||||
|
}
|
||||||
|
return that.tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheRedis) reCon() bool {
|
||||||
|
var err error
|
||||||
|
that.conn, err = redis.Dial("tcp", that.Host+":"+ObjToStr(that.Port))
|
||||||
|
if err != nil {
|
||||||
|
that.conn = nil
|
||||||
|
that.Error.SetError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if that.Pwd != "" {
|
||||||
|
_, err = that.conn.Do("AUTH", that.Pwd)
|
||||||
|
if err != nil {
|
||||||
|
that.conn = nil
|
||||||
|
that.Error.SetError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (that *CacheRedis) del(key string) {
|
||||||
|
del := strings.Index(key, "*")
|
||||||
|
if del != -1 {
|
||||||
|
val, err := redis.Strings(that.conn.Do("KEYS", key))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
that.conn.Send("MULTI")
|
||||||
|
for i, _ := range val {
|
||||||
|
that.conn.Send("DEL", val[i])
|
||||||
|
}
|
||||||
|
that.conn.Do("EXEC")
|
||||||
|
} else {
|
||||||
|
_, err := that.conn.Do("DEL", key)
|
||||||
|
if err != nil {
|
||||||
|
that.Error.SetError(err)
|
||||||
|
_, err = that.conn.Do("PING")
|
||||||
|
if err != nil {
|
||||||
|
if that.reCon() {
|
||||||
|
_, err = that.conn.Do("DEL", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//key value ,时间为时间戳
|
||||||
|
func (that *CacheRedis) set(key string, value string, time int64) {
|
||||||
|
_, err := that.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
that.Error.SetError(err)
|
||||||
|
_, err = that.conn.Do("PING")
|
||||||
|
if err != nil {
|
||||||
|
if that.reCon() {
|
||||||
|
_, err = that.conn.Do("SET", key, value, "EX", ObjToStr(time))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheRedis) get(key string) *Obj {
|
||||||
|
reData := &Obj{}
|
||||||
|
var err error
|
||||||
|
reData.Data, err = redis.String(that.conn.Do("GET", key))
|
||||||
|
if err != nil {
|
||||||
|
reData.Data = nil
|
||||||
|
if !strings.Contains(err.Error(), "nil returned") {
|
||||||
|
that.Error.SetError(err)
|
||||||
|
_, err = that.conn.Do("PING")
|
||||||
|
if err != nil {
|
||||||
|
if that.reCon() {
|
||||||
|
reData.Data, err = redis.String(that.conn.Do("GET", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *CacheRedis) Cache(key string, data ...interface{}) *Obj {
|
||||||
|
reData := &Obj{}
|
||||||
|
if that.conn == nil {
|
||||||
|
re := that.reCon()
|
||||||
|
if !re {
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//查询缓存
|
||||||
|
if len(data) == 0 {
|
||||||
|
|
||||||
|
reData = that.get(key)
|
||||||
|
return reData
|
||||||
|
|
||||||
|
}
|
||||||
|
tim := int64(0)
|
||||||
|
//删除缓存
|
||||||
|
if len(data) == 1 && data[0] == nil {
|
||||||
|
that.del(key)
|
||||||
|
return reData
|
||||||
|
}
|
||||||
|
//添加缓存
|
||||||
|
if len(data) == 1 {
|
||||||
|
|
||||||
|
if that.TimeOut == 0 {
|
||||||
|
//that.Time = Config.GetInt64("cacheShortTime")
|
||||||
|
}
|
||||||
|
|
||||||
|
tim += that.TimeOut
|
||||||
|
}
|
||||||
|
if len(data) == 2 {
|
||||||
|
that.Error.SetError(nil)
|
||||||
|
tempt := ObjToInt64(data[1], that.Error)
|
||||||
|
if tempt > tim {
|
||||||
|
|
||||||
|
tim = tempt
|
||||||
|
} else if that.GetError() == nil {
|
||||||
|
|
||||||
|
tim = tim + tempt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.set(key, ObjToStr(data[0]), tim)
|
||||||
|
|
||||||
|
return reData
|
||||||
|
|
||||||
|
}
|
||||||
20
vendor/code.hoteas.com/golang/hotime/cache/type.go
generated
vendored
Normal file
20
vendor/code.hoteas.com/golang/hotime/cache/type.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheIns interface {
|
||||||
|
//set(key string, value interface{}, time int64)
|
||||||
|
//get(key string) interface{}
|
||||||
|
//delete(key string)
|
||||||
|
GetError() *Error
|
||||||
|
SetError(err *Error)
|
||||||
|
Cache(key string, data ...interface{}) *Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
//单条缓存数据
|
||||||
|
type cacheData struct {
|
||||||
|
time int64
|
||||||
|
data interface{}
|
||||||
|
}
|
||||||
1054
vendor/code.hoteas.com/golang/hotime/code.go
generated
vendored
Normal file
1054
vendor/code.hoteas.com/golang/hotime/code.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
188
vendor/code.hoteas.com/golang/hotime/code/config.go
generated
vendored
Normal file
188
vendor/code.hoteas.com/golang/hotime/code/config.go
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package code
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Config = Map{
|
||||||
|
"name": "HoTimeDashBoard",
|
||||||
|
//"id": "2f92h3herh23rh2y8",
|
||||||
|
"label": "HoTime管理平台",
|
||||||
|
"stop": Slice{"role", "org"}, //不更新的,同时不允许修改用户自身对应的表数据
|
||||||
|
"labelConfig": Map{
|
||||||
|
"show": "开启",
|
||||||
|
"add": "添加",
|
||||||
|
"delete": "删除",
|
||||||
|
"edit": "编辑",
|
||||||
|
"info": "查看详情",
|
||||||
|
"download": "下载清单",
|
||||||
|
},
|
||||||
|
"menus": []Map{
|
||||||
|
//{"label": "平台首页", "name": "HelloWorld", "icon": "el-icon-s-home"},
|
||||||
|
//{"label": "测试表格", "table": "table", "icon": "el-icon-suitcase"},
|
||||||
|
//{"label": "系统管理", "name": "setting", "icon": "el-icon-setting",
|
||||||
|
// "menus": []Map{
|
||||||
|
// {"label": "用户管理", "table": "user",
|
||||||
|
// "default": {
|
||||||
|
// "path": "info",
|
||||||
|
// "id": "1"
|
||||||
|
// },
|
||||||
|
// "auth": ["show","edit","info","add","delete"],
|
||||||
|
// },
|
||||||
|
// {"label": "组织管理", "table": "organization"},
|
||||||
|
// {"label": "地区管理", "table": "area"},
|
||||||
|
// {"label": "角色管理", "table": "role"},
|
||||||
|
// {"label": "日志管理", "table": "log"},
|
||||||
|
// {"label": "系统设置", "table": "system", "default": "edit"},
|
||||||
|
// },
|
||||||
|
//},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var DefaultMenuParentName = "sys"
|
||||||
|
|
||||||
|
var ColumnDataType = map[string]string{
|
||||||
|
//sqlite专有类型
|
||||||
|
"real": "number",
|
||||||
|
//mysql数据类型宽泛类型
|
||||||
|
"int": "number",
|
||||||
|
"float": "number",
|
||||||
|
"double": "number",
|
||||||
|
"decimal": "number",
|
||||||
|
"integer": "number", //sqlite3
|
||||||
|
"char": "text",
|
||||||
|
"text": "text",
|
||||||
|
"blob": "text",
|
||||||
|
"date": "time",
|
||||||
|
"time": "time",
|
||||||
|
"year": "time",
|
||||||
|
"geometry": "gis", //不建议使用gis类型,建议使用其他代替
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColumnShow struct {
|
||||||
|
Name string //名称
|
||||||
|
|
||||||
|
List bool //列表权限
|
||||||
|
Edit bool //新增和编辑权限
|
||||||
|
Info bool //详情权限
|
||||||
|
Must bool //字段全匹配
|
||||||
|
Type string //空字符串表示
|
||||||
|
Strict bool //name严格匹配必须是这个词才行
|
||||||
|
}
|
||||||
|
|
||||||
|
var RuleConfig = []Map{
|
||||||
|
{"name": "idcard", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "id", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": true, "type": ""},
|
||||||
|
{"name": "sn", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "parent_ids", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": true, "type": "index"},
|
||||||
|
{"name": "index", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": true, "type": "index"},
|
||||||
|
|
||||||
|
{"name": "parent_id", "add": true, "list": true, "edit": true, "info": true, "must": false, "true": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "amount", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": true, "type": "money"},
|
||||||
|
|
||||||
|
{"name": "info", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "textArea"},
|
||||||
|
|
||||||
|
{"name": "status", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||||
|
{"name": "state", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||||
|
{"name": "sex", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "select"},
|
||||||
|
|
||||||
|
{"name": "delete", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "lat", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "lng", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "latitude", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "longitude", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "password", "add": true, "list": false, "edit": true, "info": false, "must": false, "strict": false, "type": "password"},
|
||||||
|
{"name": "pwd", "add": true, "list": false, "edit": true, "info": false, "must": false, "strict": false, "type": "password"},
|
||||||
|
|
||||||
|
{"name": "version", "add": false, "list": false, "edit": false, "info": false, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "seq", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "sort", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "note", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "description", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "abstract", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "content", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "textArea"},
|
||||||
|
|
||||||
|
{"name": "address", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "full_name", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
|
||||||
|
{"name": "create_time", "add": false, "list": false, "edit": false, "info": true, "must": false, "strict": true, "type": "time"},
|
||||||
|
{"name": "modify_time", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": true, "type": "time"},
|
||||||
|
|
||||||
|
{"name": "image", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||||
|
|
||||||
|
{"name": "img", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||||
|
{"name": "avatar", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||||
|
{"name": "icon", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "image"},
|
||||||
|
|
||||||
|
{"name": "file", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "file"},
|
||||||
|
|
||||||
|
{"name": "age", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "email", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "time", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "time"},
|
||||||
|
|
||||||
|
{"name": "level", "add": false, "list": false, "edit": false, "info": true, "must": false, "strict": false, "type": ""},
|
||||||
|
{"name": "rule", "add": true, "list": true, "edit": true, "info": true, "must": false, "strict": false, "type": "form"},
|
||||||
|
|
||||||
|
{"name": "auth", "add": true, "list": false, "edit": true, "info": true, "must": false, "strict": false, "type": "auth"},
|
||||||
|
|
||||||
|
{"name": "table", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": "table"},
|
||||||
|
{"name": "table_id", "add": false, "list": true, "edit": false, "info": true, "must": false, "strict": false, "type": "table_id"},
|
||||||
|
}
|
||||||
|
|
||||||
|
//var ColumnNameType = []ColumnShow{
|
||||||
|
// //通用
|
||||||
|
// {"idcard", false, true, true, false, "", false},
|
||||||
|
// {"id", true, false, true, false, "", true},
|
||||||
|
// {"sn", true, false, true, false, "", false},
|
||||||
|
// {"parent_ids", false, false, false, false, "index", true},
|
||||||
|
// {"parent_id", true, true, true, false, "", true},
|
||||||
|
// {"amount", true, true, true, false, "money", true},
|
||||||
|
// {"info", false, true, true, false, "textArea", false},
|
||||||
|
// //"sn"{true,true,true,""},
|
||||||
|
// {"status", true, true, true, false, "select", false},
|
||||||
|
// {"state", true, true, true, false, "select", false},
|
||||||
|
// {"sex", true, true, true, false, "select", false},
|
||||||
|
// {"delete", false, false, false, false, "", false},
|
||||||
|
//
|
||||||
|
// {"lat", false, true, true, false, "", false},
|
||||||
|
// {"lng", false, true, true, false, "", false},
|
||||||
|
// {"latitude", false, true, true, false, "", false},
|
||||||
|
// {"longitude", false, true, true, false, "", false},
|
||||||
|
//
|
||||||
|
// {"index", false, false, false, false, "index", false},
|
||||||
|
//
|
||||||
|
// {"password", false, true, false, false, "password", false},
|
||||||
|
// {"pwd", false, true, false, false, "password", false},
|
||||||
|
//
|
||||||
|
// {"version", false, false, false, false, "", false},
|
||||||
|
// {"seq", false, true, true, false, "", false},
|
||||||
|
// {"sort", false, true, true, false, "", false},
|
||||||
|
// {"note", false, true, true, false, "", false},
|
||||||
|
// {"description", false, true, true, false, "", false},
|
||||||
|
// {"abstract", false, true, true, false, "", false},
|
||||||
|
// {"content", false, true, true, false, "textArea", false},
|
||||||
|
// {"address", true, true, true, false, "", false},
|
||||||
|
// {"full_name", false, true, true, false, "", false},
|
||||||
|
// {"create_time", false, false, true, false, "time", true},
|
||||||
|
// {"modify_time", true, false, true, false, "time", true},
|
||||||
|
// {"image", false, true, true, false, "image", false},
|
||||||
|
// {"img", false, true, true, false, "image", false},
|
||||||
|
// {"icon", false, true, true, false, "image", false},
|
||||||
|
// {"avatar", false, true, true, false, "image", false},
|
||||||
|
// {"file", false, true, true, false, "file", false},
|
||||||
|
// {"age", false, true, true, false, "", false},
|
||||||
|
// {"email", false, true, true, false, "", false},
|
||||||
|
// {"time", true, true, true, false, "time", false},
|
||||||
|
// {"level", false, false, true, false, "", false},
|
||||||
|
// {"rule", true, true, true, false, "form", false},
|
||||||
|
// {"auth", false, true, true, false, "auth", true},
|
||||||
|
// {"table", true, false, true, false, "table", false},
|
||||||
|
// {"table_id", true, false, true, false, "table_id", false},
|
||||||
|
//}
|
||||||
1277
vendor/code.hoteas.com/golang/hotime/code/makecode.go
generated
vendored
Normal file
1277
vendor/code.hoteas.com/golang/hotime/code/makecode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
236
vendor/code.hoteas.com/golang/hotime/code/template.go
generated
vendored
Normal file
236
vendor/code.hoteas.com/golang/hotime/code/template.go
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package code
|
||||||
|
|
||||||
|
var InitTpt = `package {{name}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime"
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ID = "{{id}}"
|
||||||
|
|
||||||
|
// Project 管理端项目
|
||||||
|
var Project = Proj{
|
||||||
|
//"user": UserCtr,
|
||||||
|
{{tablesCtr}}
|
||||||
|
"hotime":Ctr{
|
||||||
|
"login": func(that *Context) {
|
||||||
|
|
||||||
|
name := that.Req.FormValue("name")
|
||||||
|
password := that.Req.FormValue("password")
|
||||||
|
if name == "" || password == "" {
|
||||||
|
that.Display(3, "参数不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user := that.Db.Get("admin", "*", Map{"AND": Map{"OR":Map{"name": name,"phone":name}, "password": Md5(password)}})
|
||||||
|
if user == nil {
|
||||||
|
that.Display(5, "登录失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
that.Session("admin_id", user.GetCeilInt("id"))
|
||||||
|
that.Session("admin_name", name)
|
||||||
|
that.Display(0, that.SessionId)
|
||||||
|
},
|
||||||
|
"logout": func(that *Context) {
|
||||||
|
that.Session("admin_id", nil)
|
||||||
|
that.Session("admin_name", nil)
|
||||||
|
that.Display(0, "退出登录成功")
|
||||||
|
},
|
||||||
|
"info": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||||
|
str, inData := that.MakeCodeRouter[hotimeName].Info("admin", data, that.Db)
|
||||||
|
where := Map{"id": that.Session("admin_id").ToCeilInt()}
|
||||||
|
if len(inData) ==1 {
|
||||||
|
inData["id"] =where["id"]
|
||||||
|
where = Map{"AND": inData}
|
||||||
|
}else if len(inData) >1 {
|
||||||
|
where["OR"]=inData
|
||||||
|
where = Map{"AND": where}
|
||||||
|
}
|
||||||
|
re := that.Db.Get("admin", str, where)
|
||||||
|
if re == nil {
|
||||||
|
that.Display(4, "找不到对应信息")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range re {
|
||||||
|
column := that.MakeCodeRouter[hotimeName].TableColumns["admin"][k]
|
||||||
|
if column == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||||
|
re[column.GetString("link")] = that.Db.Get(column.GetString("link"),"id," +column.GetString("value"), Map{"id": v})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Display(0, re)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var CtrTpt = `package {{name}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime"
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var {{table}}Ctr = Ctr{
|
||||||
|
"info": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||||
|
str, inData := that.MakeCodeRouter[hotimeName].Info(that.RouterString[1], data, that.Db)
|
||||||
|
where := Map{"id": that.RouterString[2]}
|
||||||
|
|
||||||
|
if len(inData) ==1 {
|
||||||
|
inData["id"] =where["id"]
|
||||||
|
where = Map{"AND": inData}
|
||||||
|
}else if len(inData) >1 {
|
||||||
|
where["OR"]=inData
|
||||||
|
where = Map{"AND": where}
|
||||||
|
}
|
||||||
|
|
||||||
|
re := that.Db.Get(that.RouterString[1], str, where)
|
||||||
|
|
||||||
|
if re == nil {
|
||||||
|
that.Display(4, "找不到对应信息")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range re {
|
||||||
|
column := that.MakeCodeRouter[hotimeName].TableColumns[that.RouterString[1]][k]
|
||||||
|
if column == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (column["list"] == nil || column.GetBool("list")) && column.GetString("link") != "" {
|
||||||
|
re[column.GetString("link")] = that.Db.Get(column.GetString("link"), "id,"+column.GetString("value"), Map{"id": v})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Display(0, re)
|
||||||
|
},
|
||||||
|
"add": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
inData := that.MakeCodeRouter[hotimeName].Add(that.RouterString[1], that.Req)
|
||||||
|
if inData == nil {
|
||||||
|
that.Display(3, "请求参数不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
re := that.Db.Insert(that.RouterString[1], inData)
|
||||||
|
|
||||||
|
if re == 0 {
|
||||||
|
that.Display(4, "无法插入对应数据")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//索引管理,便于检索以及权限
|
||||||
|
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||||
|
index := that.Db.Get(that.RouterString[1], "` + "`index`" + `", Map{"id": inData.Get("parent_id")})
|
||||||
|
inData["index"] = index.GetString("index")+ObjToStr(re)+","
|
||||||
|
that.Db.Update(that.RouterString[1],Map{"index":inData["index"]},Map{"id":re})
|
||||||
|
}else if inData.GetString("index") != ""{
|
||||||
|
inData["index"] = "," + ObjToStr(re) + ","
|
||||||
|
that.Db.Update(that.RouterString[1], Map{"index": inData["index"]}, Map{"id": re})
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Display(0, re)
|
||||||
|
},
|
||||||
|
"update": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
inData := that.MakeCodeRouter[hotimeName].Edit(that.RouterString[1], that.Req)
|
||||||
|
if inData == nil {
|
||||||
|
that.Display(3, "没有找到要更新的数据")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//索引管理,便于检索以及权限
|
||||||
|
if inData.Get("parent_id") != nil && inData.GetString("index") != "" {
|
||||||
|
Index := that.Db.Get(that.RouterString[1], "` + "`index`" + `", Map{"id": that.RouterString[2]})
|
||||||
|
parentIndex := that.Db.Get(that.RouterString[1], "` + "`index`" + `", Map{"id": inData.Get("parent_id")})
|
||||||
|
inData["index"] = parentIndex.GetString("index") + that.RouterString[2] + ","
|
||||||
|
|
||||||
|
childNodes := that.Db.Select(that.RouterString[1], "id,` + "`index`" + `", Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||||
|
|
||||||
|
for _, v := range childNodes {
|
||||||
|
v["index"] = strings.Replace(v.GetString("index"), Index.GetString("index"), inData.GetString("index"), -1)
|
||||||
|
that.Db.Update(that.RouterString[1], Map{"index": v["index"]}, Map{"id": v.GetCeilInt("id")})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
re := that.Db.Update(that.RouterString[1], inData, Map{"id": that.RouterString[2]})
|
||||||
|
|
||||||
|
if re == 0 {
|
||||||
|
that.Display(4, "更新数据失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Display(0, re)
|
||||||
|
},
|
||||||
|
"remove": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
inData := that.MakeCodeRouter[hotimeName].Delete(that.RouterString[1], that.Req)
|
||||||
|
if inData == nil {
|
||||||
|
that.Display(3, "请求参数不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
re :=int64(0)
|
||||||
|
//索引管理,便于检索以及权限
|
||||||
|
if inData.Get("parent_id") != nil && inData.GetSlice("index") != nil {
|
||||||
|
re=that.Db.Delete(that.RouterString[1], Map{"index[~]": "," + that.RouterString[2] + ","})
|
||||||
|
}else {
|
||||||
|
re=that.Db.Delete(that.RouterString[1], Map{"id": that.RouterString[2]})
|
||||||
|
}
|
||||||
|
|
||||||
|
if re == 0 {
|
||||||
|
that.Display(4, "删除数据失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
that.Display(0, "删除成功")
|
||||||
|
},
|
||||||
|
|
||||||
|
"search": func(that *Context) {
|
||||||
|
hotimeName := that.RouterString[0]
|
||||||
|
data := that.Db.Get("admin", "*", Map{"id": that.Session("admin_id").ToCeilInt()})
|
||||||
|
|
||||||
|
columnStr, leftJoin, where := that.MakeCodeRouter[hotimeName].Search(that.RouterString[1], data, that.Req, that.Db)
|
||||||
|
|
||||||
|
page := ObjToInt(that.Req.FormValue("page"))
|
||||||
|
pageSize := ObjToInt(that.Req.FormValue("pageSize"))
|
||||||
|
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
count := that.Db.Count(that.RouterString[1], leftJoin, where)
|
||||||
|
reData := that.Db.Page(page, pageSize).
|
||||||
|
PageSelect(that.RouterString[1], leftJoin, columnStr, where)
|
||||||
|
|
||||||
|
for _, v := range reData {
|
||||||
|
for k, _ := range v {
|
||||||
|
column := that.MakeCodeRouter[hotimeName].TableColumns[that.RouterString[1]][k]
|
||||||
|
if column == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if column["list"] != false && column["name"] == "parent_id" && column.GetString("link") != "" {
|
||||||
|
parentC := that.Db.Get(column.GetString("link"), column.GetString("value"), Map{"id": v.GetCeilInt(k)})
|
||||||
|
v[column.GetString("link")+"_"+column.GetString("name")+"_"+column.GetString("value")] = ""
|
||||||
|
if parentC != nil {
|
||||||
|
v[column.GetString("link")+"_"+column.GetString("name")+"_"+column.GetString("value")] = parentC.GetString(column.GetString("value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Display(0, Map{"count": count, "data": reData})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`
|
||||||
18
vendor/code.hoteas.com/golang/hotime/common/context_base.go
generated
vendored
Normal file
18
vendor/code.hoteas.com/golang/hotime/common/context_base.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContextBase struct {
|
||||||
|
tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
//唯一标志
|
||||||
|
func (that *ContextBase) GetTag() string {
|
||||||
|
|
||||||
|
if that.tag == "" {
|
||||||
|
that.tag = ObjToStr(time.Now().Unix()) + ":" + ObjToStr(Random())
|
||||||
|
}
|
||||||
|
return that.tag
|
||||||
|
}
|
||||||
27
vendor/code.hoteas.com/golang/hotime/common/error.go
generated
vendored
Normal file
27
vendor/code.hoteas.com/golang/hotime/common/error.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error 框架层处理错误
|
||||||
|
type Error struct {
|
||||||
|
Logger *logrus.Logger
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Error) GetError() error {
|
||||||
|
|
||||||
|
return that.error
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Error) SetError(err error) {
|
||||||
|
that.error = err
|
||||||
|
if that.Logger != nil && err != nil {
|
||||||
|
//that.Logger=log.GetLog("",false)
|
||||||
|
that.Logger.Warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
374
vendor/code.hoteas.com/golang/hotime/common/func.go
generated
vendored
Normal file
374
vendor/code.hoteas.com/golang/hotime/common/func.go
generated
vendored
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//安全锁
|
||||||
|
//func SafeMutex(tag int, f func() interface{}) interface{} {
|
||||||
|
//
|
||||||
|
// mutexer.Lock()
|
||||||
|
// if mutex[tag] == nil {
|
||||||
|
//
|
||||||
|
// mutex[tag] = &sync.RWMutex{}
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// mutexer.Unlock()
|
||||||
|
//
|
||||||
|
// mutex[tag].Lock()
|
||||||
|
// res := f()
|
||||||
|
// mutex[tag].Unlock()
|
||||||
|
// return res
|
||||||
|
//}
|
||||||
|
|
||||||
|
// StrFirstToUpper 字符串首字符大写
|
||||||
|
func StrFirstToUpper(str string) string {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
first := Substr(str, 0, 1)
|
||||||
|
other := Substr(str, 1, len(str)-1)
|
||||||
|
|
||||||
|
return strings.ToUpper(first) + other
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间转字符串,第二个参数支持1-5对应显示年月日时分秒
|
||||||
|
func Time2Str(t time.Time, qu ...interface{}) string {
|
||||||
|
if t.Unix() < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
tp := 5
|
||||||
|
if len(qu) != 0 {
|
||||||
|
tp = (qu[0]).(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tp {
|
||||||
|
case 1:
|
||||||
|
return t.Format("2006-01")
|
||||||
|
case 2:
|
||||||
|
return t.Format("2006-01-02")
|
||||||
|
case 3:
|
||||||
|
return t.Format("2006-01-02 15")
|
||||||
|
case 4:
|
||||||
|
return t.Format("2006-01-02 15:04")
|
||||||
|
case 5:
|
||||||
|
return t.Format("2006-01-02 15:04:05")
|
||||||
|
case 12:
|
||||||
|
return t.Format("01-02")
|
||||||
|
case 14:
|
||||||
|
return t.Format("01-02 15:04")
|
||||||
|
case 15:
|
||||||
|
return t.Format("01-02 15:04:05")
|
||||||
|
case 34:
|
||||||
|
return t.Format("15:04")
|
||||||
|
case 35:
|
||||||
|
return t.Format("15:04:05")
|
||||||
|
}
|
||||||
|
return t.Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrLd 相似度计算 ld compares two strings and returns the levenshtein distance between them.
|
||||||
|
func StrLd(s, t string, ignoreCase bool) int {
|
||||||
|
if ignoreCase {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
t = strings.ToLower(t)
|
||||||
|
}
|
||||||
|
d := make([][]int, len(s)+1)
|
||||||
|
for i := range d {
|
||||||
|
d[i] = make([]int, len(t)+1)
|
||||||
|
}
|
||||||
|
for i := range d {
|
||||||
|
d[i][0] = i
|
||||||
|
}
|
||||||
|
for j := range d[0] {
|
||||||
|
d[0][j] = j
|
||||||
|
}
|
||||||
|
for j := 1; j <= len(t); j++ {
|
||||||
|
for i := 1; i <= len(s); i++ {
|
||||||
|
if s[i-1] == t[j-1] {
|
||||||
|
d[i][j] = d[i-1][j-1]
|
||||||
|
} else {
|
||||||
|
min := d[i-1][j]
|
||||||
|
if d[i][j-1] < min {
|
||||||
|
min = d[i][j-1]
|
||||||
|
}
|
||||||
|
if d[i-1][j-1] < min {
|
||||||
|
min = d[i-1][j-1]
|
||||||
|
}
|
||||||
|
d[i][j] = min + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return d[len(s)][len(t)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substr 字符串截取
|
||||||
|
func Substr(str string, start int, length int) string {
|
||||||
|
rs := []rune(str)
|
||||||
|
rl := len(rs)
|
||||||
|
end := 0
|
||||||
|
|
||||||
|
if start < 0 {
|
||||||
|
start = rl - 1 + start
|
||||||
|
}
|
||||||
|
end = start + length
|
||||||
|
|
||||||
|
if start > end {
|
||||||
|
start, end = end, start
|
||||||
|
}
|
||||||
|
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
if start > rl {
|
||||||
|
start = rl
|
||||||
|
}
|
||||||
|
if end < 0 {
|
||||||
|
end = 0
|
||||||
|
}
|
||||||
|
if end > rl {
|
||||||
|
end = rl
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(rs[start:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexLastStr 获取最后出现字符串的下标
|
||||||
|
//return 找不到返回 -1
|
||||||
|
func IndexLastStr(str, sep string) int {
|
||||||
|
sepSlice := []rune(sep)
|
||||||
|
strSlice := []rune(str)
|
||||||
|
if len(sepSlice) > len(strSlice) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
v := sepSlice[len(sepSlice)-1]
|
||||||
|
|
||||||
|
for i := len(strSlice) - 1; i >= 0; i-- {
|
||||||
|
vs := strSlice[i]
|
||||||
|
if v == vs {
|
||||||
|
j := len(sepSlice) - 2
|
||||||
|
for ; j >= 0; j-- {
|
||||||
|
vj := sepSlice[j]
|
||||||
|
vsj := strSlice[i-(len(sepSlice)-j-1)]
|
||||||
|
if vj != vsj {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if j < 0 {
|
||||||
|
return i - len(sepSlice) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Md5 md5
|
||||||
|
func Md5(req string) string {
|
||||||
|
md5Ctx := md5.New()
|
||||||
|
md5Ctx.Write([]byte(req))
|
||||||
|
cipherStr := md5Ctx.Sum(nil)
|
||||||
|
return hex.EncodeToString(cipherStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rand 随机数
|
||||||
|
func Rand(count int) int {
|
||||||
|
res := Random()
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
res = res * 10
|
||||||
|
}
|
||||||
|
return ObjToInt(res)
|
||||||
|
}
|
||||||
|
func Random() float64 {
|
||||||
|
v := float64(0)
|
||||||
|
m := float64(0.1)
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
facter := map[int]int{4: 1, 9: 1, 2: 1, 3: 1, 1: 1, 7: 1, 0: 1, 5: 1, 6: 1, 8: 1}
|
||||||
|
for k, _ := range facter {
|
||||||
|
|
||||||
|
v = v + float64(k)*m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m = m * 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandX 随机数范围
|
||||||
|
func RandX(small int, max int) int {
|
||||||
|
res := 0
|
||||||
|
//随机对象
|
||||||
|
if small == max {
|
||||||
|
return small
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
res = ObjToInt(Random() * float64(max+1))
|
||||||
|
if res >= small {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
////路由
|
||||||
|
//func Router(ctr CtrInterface) {
|
||||||
|
//
|
||||||
|
// str := reflect.ValueOf(ctr).Type().String()
|
||||||
|
// a := strings.IndexByte(str, '.')
|
||||||
|
//
|
||||||
|
// c := len(str) - len("Ctr") - 1
|
||||||
|
//
|
||||||
|
// app := Substr(str, 0, a) //属于哪个app
|
||||||
|
// ct := Substr(str, a+1, c-a) //属于哪个控制器
|
||||||
|
// var x = map[string]CtrInterface{}
|
||||||
|
// if _, ok := Proj[app]; ok {
|
||||||
|
// //存在APP
|
||||||
|
// x = Proj[app]
|
||||||
|
// } else {
|
||||||
|
// x = map[string]CtrInterface{}
|
||||||
|
// }
|
||||||
|
// x[ct] = ctr //将控制器存入APP
|
||||||
|
// // fmt.Println(c)
|
||||||
|
// Proj[app] = x //将APP存入测试
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func RunMethodListener(test func(app []string)) {
|
||||||
|
// RunMethodListenerFunc = test
|
||||||
|
//}
|
||||||
|
|
||||||
|
//func SetDb(db *sql.DB) {
|
||||||
|
// db.SetMaxOpenConns(2000)
|
||||||
|
// db.SetMaxIdleConns(1000)
|
||||||
|
// db.Ping()
|
||||||
|
// SqlDB = &*db
|
||||||
|
// GetDb()
|
||||||
|
//}
|
||||||
|
|
||||||
|
// DeepCopyMap 复制返回数组
|
||||||
|
func DeepCopyMap(value interface{}) interface{} {
|
||||||
|
if valueMap, ok := value.(Map); ok {
|
||||||
|
newMap := make(Map)
|
||||||
|
for k, v := range valueMap {
|
||||||
|
newMap[k] = DeepCopyMap(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMap
|
||||||
|
} else if valueSlice, ok := value.([]interface{}); ok {
|
||||||
|
newSlice := make([]interface{}, len(valueSlice))
|
||||||
|
for k, v := range valueSlice {
|
||||||
|
newSlice[k] = DeepCopyMap(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSlice
|
||||||
|
} else if valueMap, ok := value.(map[string]interface{}); ok {
|
||||||
|
newMap := make(map[string]interface{})
|
||||||
|
for k, v := range valueMap {
|
||||||
|
newMap[k] = DeepCopyMap(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMap
|
||||||
|
|
||||||
|
} else if valueSlice, ok := value.(Slice); ok {
|
||||||
|
newSlice := make(Slice, len(valueSlice))
|
||||||
|
for k, v := range valueSlice {
|
||||||
|
newSlice[k] = DeepCopyMap(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
////获取数据库
|
||||||
|
//func GetDb() (HoTimeDB, error) {
|
||||||
|
// Db.DB = &*SqlDB
|
||||||
|
// Db.Cached = true
|
||||||
|
// return Db, nil
|
||||||
|
//}
|
||||||
|
|
||||||
|
//初始化方法
|
||||||
|
//func Init() {
|
||||||
|
//
|
||||||
|
// http.HandleFunc("/", PublicCore.myHandler)
|
||||||
|
//
|
||||||
|
// InitCache()
|
||||||
|
// http.ListenAndServe(":"+Config["port"].(string), nil)
|
||||||
|
//}
|
||||||
|
|
||||||
|
////设置Config
|
||||||
|
//func SetCfg(tData Map) {
|
||||||
|
// for k, v := range tData {
|
||||||
|
// Config[k] = v
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Round 浮点数四舍五入保留小数
|
||||||
|
func Round(f float64, n int) float64 {
|
||||||
|
pow10_n := math.Pow10(n)
|
||||||
|
return math.Trunc((f+0.5/pow10_n)*pow10_n) / pow10_n
|
||||||
|
}
|
||||||
|
|
||||||
|
//func InitDbCache(tim int64) {
|
||||||
|
// res := Db.Query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='" + Config.GetString("dbName") + "' AND TABLE_NAME='cached'")
|
||||||
|
// if len(res) == 0 {
|
||||||
|
// Db.Exec("CREATE TABLE `cached` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `ckey` varchar(60) DEFAULT NULL, `cvalue` varchar(2000) DEFAULT NULL, `time` bigint(20) DEFAULT NULL, `endtime` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=198740 DEFAULT CHARSET=utf8")
|
||||||
|
// }
|
||||||
|
// cacheConfig := Config.GetMap("cacheConfig").GetObj("db").(CacheConfg)
|
||||||
|
// cacheConfig.Used = true
|
||||||
|
// if tim != 0 {
|
||||||
|
// cacheConfig.Time = tim
|
||||||
|
// }
|
||||||
|
// Config.GetMap("cacheConfig")["db"] = cacheConfig
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func Cache(key interface{}, data ...interface{}) interface{} {
|
||||||
|
// cachePRI := Config["cachePRI"].([]string)
|
||||||
|
// var res interface{}
|
||||||
|
// for i := 0; i < len(cachePRI); i++ {
|
||||||
|
// if cachePRI[i] == "ridis" && Config["cacheConfig"].(Map)["ridis"].(CacheConfg).Used {
|
||||||
|
// res = CacheMemIns.Cache(ObjToStr(key), data...)
|
||||||
|
// }
|
||||||
|
// if cachePRI[i] == "db" && Config["cacheConfig"].(Map)["db"].(CacheConfg).Used {
|
||||||
|
// res = CacheDBIns.Cache(ObjToStr(key), data...)
|
||||||
|
// }
|
||||||
|
// if cachePRI[i] == "memory" && Config["cacheConfig"].(Map)["memory"].(CacheConfg).Used {
|
||||||
|
// res = CacheMemIns.Cache(ObjToStr(key), data...)
|
||||||
|
// }
|
||||||
|
// //将缓存送到前面的可缓存仓库去
|
||||||
|
// if res != nil {
|
||||||
|
// for j := 0; j < i; j++ {
|
||||||
|
// if cachePRI[j] == "ridis" && Config["cacheConfig"].(Map)["ridis"].(CacheConfg).Used {
|
||||||
|
// CacheMemIns.Cache(ObjToStr(key), res)
|
||||||
|
// }
|
||||||
|
// if cachePRI[j] == "db" && Config["cacheConfig"].(Map)["db"].(CacheConfg).Used {
|
||||||
|
// CacheDBIns.Cache(ObjToStr(key), res)
|
||||||
|
// }
|
||||||
|
// if cachePRI[j] == "memory" && Config["cacheConfig"].(Map)["memory"].(CacheConfg).Used {
|
||||||
|
// CacheMemIns.Cache(ObjToStr(key), res)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// return res
|
||||||
|
//}
|
||||||
|
|
||||||
|
//func InitCache() {
|
||||||
|
// CacheMemIns.Init(Config["cacheConfig"].(Map)["memory"].(CacheConfg).Time)
|
||||||
|
// CacheDBIns.Init(Config["cacheConfig"].(Map)["db"].(CacheConfg).Time)
|
||||||
|
//}
|
||||||
189
vendor/code.hoteas.com/golang/hotime/common/map.go
generated
vendored
Normal file
189
vendor/code.hoteas.com/golang/hotime/common/map.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//hotime的常用map
|
||||||
|
type Map map[string]interface{}
|
||||||
|
|
||||||
|
//获取string
|
||||||
|
func (that Map) GetString(key string, err ...*Error) string {
|
||||||
|
|
||||||
|
if len(err) != 0 {
|
||||||
|
err[0].SetError(nil)
|
||||||
|
}
|
||||||
|
return ObjToStr((that)[key])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Map) Pointer() *Map {
|
||||||
|
|
||||||
|
return that
|
||||||
|
}
|
||||||
|
|
||||||
|
//增加接口
|
||||||
|
func (that Map) Put(key string, value interface{}) {
|
||||||
|
//if that==nil{
|
||||||
|
// that=Map{}
|
||||||
|
//}
|
||||||
|
that[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除接口
|
||||||
|
func (that Map) Delete(key string) {
|
||||||
|
delete(that, key)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取Int
|
||||||
|
func (that Map) GetInt(key string, err ...*Error) int {
|
||||||
|
v := ObjToInt((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取Int
|
||||||
|
func (that Map) GetInt64(key string, err ...*Error) int64 {
|
||||||
|
v := ObjToInt64((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取向上取整Int64
|
||||||
|
func (that Map) GetCeilInt64(key string, err ...*Error) int64 {
|
||||||
|
v := ObjToCeilInt64((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取向上取整Int
|
||||||
|
func (that Map) GetCeilInt(key string, err ...*Error) int {
|
||||||
|
v := ObjToCeilInt((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取向上取整float64
|
||||||
|
func (that Map) GetCeilFloat64(key string, err ...*Error) float64 {
|
||||||
|
v := ObjToCeilFloat64((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取Float64
|
||||||
|
func (that Map) GetFloat64(key string, err ...*Error) float64 {
|
||||||
|
|
||||||
|
v := ObjToFloat64((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) GetSlice(key string, err ...*Error) Slice {
|
||||||
|
|
||||||
|
//var v Slice
|
||||||
|
v := ObjToSlice((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
func (that Map) GetBool(key string, err ...*Error) bool {
|
||||||
|
|
||||||
|
//var v Slice
|
||||||
|
v := ObjToBool((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) GetTime(key string, err ...*Error) time.Time {
|
||||||
|
|
||||||
|
v := ObjToTime((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) RangeSort(callback func(k string, v interface{}) (isEnd bool)) {
|
||||||
|
testQu := []string{}
|
||||||
|
//testQuData:= qu[0].(Map)
|
||||||
|
for key, _ := range that {
|
||||||
|
//fmt.Println(key, ":", value)
|
||||||
|
testQu = append(testQu, key)
|
||||||
|
}
|
||||||
|
sort.Strings(testQu)
|
||||||
|
for _, k := range testQu {
|
||||||
|
re := callback(k, that[k])
|
||||||
|
if re {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) GetMap(key string, err ...*Error) Map {
|
||||||
|
//var data Slice
|
||||||
|
|
||||||
|
v := ObjToMap((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) Get(key string, err ...*Error) interface{} {
|
||||||
|
|
||||||
|
if v, ok := (that)[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
e := errors.New("没有存储key及对应的数据")
|
||||||
|
|
||||||
|
if len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//请传递指针过来
|
||||||
|
func (that Map) ToStruct(stct interface{}) {
|
||||||
|
|
||||||
|
data := reflect.ValueOf(stct).Elem()
|
||||||
|
for k, v := range that {
|
||||||
|
ks := StrFirstToUpper(k)
|
||||||
|
dkey := data.FieldByName(ks)
|
||||||
|
if !dkey.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch dkey.Type().String() {
|
||||||
|
case "int":
|
||||||
|
dkey.SetInt(that.GetInt64(k))
|
||||||
|
case "int64":
|
||||||
|
dkey.Set(reflect.ValueOf(that.GetInt64(k)))
|
||||||
|
case "float64":
|
||||||
|
dkey.Set(reflect.ValueOf(that.GetFloat64(k)))
|
||||||
|
case "string":
|
||||||
|
dkey.Set(reflect.ValueOf(that.GetString(k)))
|
||||||
|
case "interface{}":
|
||||||
|
dkey.Set(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) ToJsonString() string {
|
||||||
|
return ObjToStr(that)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Map) JsonToMap(jsonStr string, err ...*Error) {
|
||||||
|
e := json.Unmarshal([]byte(jsonStr), &that)
|
||||||
|
if e != nil && len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
103
vendor/code.hoteas.com/golang/hotime/common/obj.go
generated
vendored
Normal file
103
vendor/code.hoteas.com/golang/hotime/common/obj.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
//对象封装方便取用
|
||||||
|
type Obj struct {
|
||||||
|
Data interface{}
|
||||||
|
Error
|
||||||
|
ContextBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) Put(data interface{}) {
|
||||||
|
that.Data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToInt(err ...Error) int {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToInt(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToTime(err ...Error) time.Time {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToTime(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToInt64(err ...Error) int64 {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToInt64(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToFloat64(err ...Error) float64 {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToFloat64(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCeilFloat64 获取向上取整float64
|
||||||
|
func (that *Obj) ToCeilFloat64(err ...*Error) float64 {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = *err[0]
|
||||||
|
}
|
||||||
|
v := ObjToCeilFloat64(that.Data, err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToStr() string {
|
||||||
|
|
||||||
|
return ObjToStr(that.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToMap(err ...Error) Map {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToMap(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToSlice(err ...Error) Slice {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToSlice(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToMapArray(err ...Error) []Map {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = err[0]
|
||||||
|
}
|
||||||
|
return ObjToMapArray(that.Data, &that.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Obj) ToObj() interface{} {
|
||||||
|
|
||||||
|
return that.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取向上取整Int64
|
||||||
|
func (that *Obj) ToCeilInt64(err ...*Error) int64 {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = *err[0]
|
||||||
|
}
|
||||||
|
v := ObjToCeilInt64(that.Data, err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取向上取整Int
|
||||||
|
func (that *Obj) ToCeilInt(err ...*Error) int {
|
||||||
|
if len(err) != 0 {
|
||||||
|
that.Error = *err[0]
|
||||||
|
}
|
||||||
|
v := ObjToCeilInt(that.Data, err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
392
vendor/code.hoteas.com/golang/hotime/common/objtoobj.go
generated
vendored
Normal file
392
vendor/code.hoteas.com/golang/hotime/common/objtoobj.go
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//仅限于hotime.Slice
|
||||||
|
func ObjToMap(obj interface{}, e ...*Error) Map {
|
||||||
|
var err error
|
||||||
|
var v Map
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
v = nil
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
} else {
|
||||||
|
switch obj.(type) {
|
||||||
|
case Map:
|
||||||
|
v = obj.(Map)
|
||||||
|
case map[string]interface{}:
|
||||||
|
v = obj.(map[string]interface{})
|
||||||
|
case string:
|
||||||
|
v = Map{}
|
||||||
|
e := json.Unmarshal([]byte(obj.(string)), &v)
|
||||||
|
if e != nil {
|
||||||
|
err = errors.New("没有合适的转换对象!" + e.Error())
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
data, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.New("没有合适的转换对象!" + err.Error())
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
v = Map{}
|
||||||
|
e := json.Unmarshal(data, &v)
|
||||||
|
if e != nil {
|
||||||
|
err = errors.New("没有合适的转换对象!" + e.Error())
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToMapArray(obj interface{}, e ...*Error) []Map {
|
||||||
|
s := ObjToSlice(obj, e...)
|
||||||
|
res := []Map{}
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
res = append(res, s.GetMap(i))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
//仅限于hotime.Slice
|
||||||
|
func ObjToSlice(obj interface{}, e ...*Error) Slice {
|
||||||
|
var err error
|
||||||
|
var v Slice
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
v = nil
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
} else {
|
||||||
|
switch obj.(type) {
|
||||||
|
case Slice:
|
||||||
|
v = obj.(Slice)
|
||||||
|
case []interface{}:
|
||||||
|
v = obj.([]interface{})
|
||||||
|
case []string:
|
||||||
|
v = Slice{}
|
||||||
|
for i := 0; i < len(obj.([]string)); i++ {
|
||||||
|
v = append(v, obj.([]string)[i])
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
v = Slice{}
|
||||||
|
err = json.Unmarshal([]byte(obj.(string)), &v)
|
||||||
|
|
||||||
|
default:
|
||||||
|
v = Slice{}
|
||||||
|
var data []byte
|
||||||
|
data, err = json.Marshal(obj)
|
||||||
|
err = json.Unmarshal(data, &v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToTime(obj interface{}, e ...*Error) time.Time {
|
||||||
|
|
||||||
|
tInt := ObjToInt64(obj)
|
||||||
|
//字符串类型,只支持标准mysql datetime格式
|
||||||
|
if tInt == 0 {
|
||||||
|
tStr := ObjToStr(obj)
|
||||||
|
|
||||||
|
if len(tStr) > 18 {
|
||||||
|
t, e := time.Parse("2006-01-02 15:04:05", tStr)
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
} else if len(tStr) > 15 {
|
||||||
|
t, e := time.Parse("2006-01-02 15:04", tStr)
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
} else if len(tStr) > 12 {
|
||||||
|
t, e := time.Parse("2006-01-02 15", tStr)
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
} else if len(tStr) > 9 {
|
||||||
|
t, e := time.Parse("2006-01-02", tStr)
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
} else if len(tStr) > 6 {
|
||||||
|
t, e := time.Parse("2006-01", tStr)
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//纳秒级别
|
||||||
|
if len(ObjToStr(tInt)) > 16 {
|
||||||
|
t := time.Time{}.Add(time.Nanosecond * time.Duration(tInt))
|
||||||
|
return t
|
||||||
|
//微秒级别
|
||||||
|
} else if len(ObjToStr(tInt)) > 13 {
|
||||||
|
t := time.Time{}.Add(time.Microsecond * time.Duration(tInt))
|
||||||
|
return t
|
||||||
|
//毫秒级别
|
||||||
|
} else if len(ObjToStr(tInt)) > 10 {
|
||||||
|
t := time.Time{}.Add(time.Millisecond * time.Duration(tInt))
|
||||||
|
return t
|
||||||
|
//秒级别
|
||||||
|
} else if len(ObjToStr(tInt)) > 9 {
|
||||||
|
t := time.Time{}.Add(time.Second * time.Duration(tInt))
|
||||||
|
return t
|
||||||
|
} else if len(ObjToStr(tInt)) > 3 {
|
||||||
|
t, e := time.Parse("2006", ObjToStr(tInt))
|
||||||
|
if e == nil {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToFloat64(obj interface{}, e ...*Error) float64 {
|
||||||
|
var err error
|
||||||
|
v := float64(0)
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
} else {
|
||||||
|
|
||||||
|
switch obj.(type) {
|
||||||
|
case int:
|
||||||
|
v = float64(obj.(int))
|
||||||
|
case int64:
|
||||||
|
v = float64(obj.(int64))
|
||||||
|
case string:
|
||||||
|
value, e := strconv.ParseFloat(obj.(string), 64)
|
||||||
|
if e != nil {
|
||||||
|
v = float64(0)
|
||||||
|
err = e
|
||||||
|
} else {
|
||||||
|
v = value
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
v = obj.(float64)
|
||||||
|
case float32:
|
||||||
|
v = float64(obj.(float32))
|
||||||
|
case uint8:
|
||||||
|
value, e := strconv.ParseFloat(obj.(string), 64)
|
||||||
|
if e != nil {
|
||||||
|
v = float64(0)
|
||||||
|
err = e
|
||||||
|
} else {
|
||||||
|
v = value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
v = float64(0)
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.IsNaN(v) {
|
||||||
|
err = errors.New("float64 is NaN")
|
||||||
|
v = 0
|
||||||
|
}
|
||||||
|
if math.IsInf(v, 0) {
|
||||||
|
err = errors.New("float64 is Inf")
|
||||||
|
v = 0
|
||||||
|
}
|
||||||
|
if len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
//向上取整
|
||||||
|
func ObjToCeilInt64(obj interface{}, e ...*Error) int64 {
|
||||||
|
f := ObjToCeilFloat64(obj, e...)
|
||||||
|
return ObjToInt64(math.Ceil(f))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//向上取整
|
||||||
|
func ObjToCeilFloat64(obj interface{}, e ...*Error) float64 {
|
||||||
|
f := ObjToFloat64(obj, e...)
|
||||||
|
return math.Ceil(f)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//向上取整
|
||||||
|
func ObjToCeilInt(obj interface{}, e ...*Error) int {
|
||||||
|
f := ObjToCeilFloat64(obj, e...)
|
||||||
|
return ObjToInt(f)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToInt64(obj interface{}, e ...*Error) int64 {
|
||||||
|
var err error
|
||||||
|
v := int64(0)
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
} else {
|
||||||
|
switch obj.(type) {
|
||||||
|
case int:
|
||||||
|
v = int64(obj.(int))
|
||||||
|
case int64:
|
||||||
|
v = obj.(int64)
|
||||||
|
case string:
|
||||||
|
value, e := StrToInt(obj.(string))
|
||||||
|
if e != nil {
|
||||||
|
v = int64(0)
|
||||||
|
err = e
|
||||||
|
} else {
|
||||||
|
v = int64(value)
|
||||||
|
}
|
||||||
|
case uint8:
|
||||||
|
value, e := StrToInt(obj.(string))
|
||||||
|
if e != nil {
|
||||||
|
v = int64(0)
|
||||||
|
err = e
|
||||||
|
} else {
|
||||||
|
v = int64(value)
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
v = int64(obj.(float64))
|
||||||
|
case float32:
|
||||||
|
v = int64(obj.(float32))
|
||||||
|
default:
|
||||||
|
v = int64(0)
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToInt(obj interface{}, e ...*Error) int {
|
||||||
|
v := ObjToInt64(obj, e...)
|
||||||
|
return int(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToBool(obj interface{}, e ...*Error) bool {
|
||||||
|
var err error
|
||||||
|
v := false
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
} else {
|
||||||
|
switch obj.(type) {
|
||||||
|
case bool:
|
||||||
|
v = obj.(bool)
|
||||||
|
default:
|
||||||
|
toInt := ObjToInt(obj)
|
||||||
|
if toInt != 0 {
|
||||||
|
v = true
|
||||||
|
}
|
||||||
|
err = errors.New("没有合适的转换对象!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjToStr(obj interface{}) string {
|
||||||
|
// fmt.Println(reflect.ValueOf(obj).Type().String() )
|
||||||
|
str := ""
|
||||||
|
if obj == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
switch obj.(type) {
|
||||||
|
case int:
|
||||||
|
str = strconv.Itoa(obj.(int))
|
||||||
|
case uint8:
|
||||||
|
str = obj.(string)
|
||||||
|
case int64:
|
||||||
|
str = strconv.FormatInt(obj.(int64), 10)
|
||||||
|
case []byte:
|
||||||
|
str = string(obj.([]byte))
|
||||||
|
case string:
|
||||||
|
str = obj.(string)
|
||||||
|
case float64:
|
||||||
|
str = strconv.FormatFloat(obj.(float64), 'f', -1, 64)
|
||||||
|
default:
|
||||||
|
strbte, err := json.MarshalIndent(obj, "", "\t")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
str = string(strbte)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
//转换为Map
|
||||||
|
func StrToMap(string string) Map {
|
||||||
|
data := Map{}
|
||||||
|
data.JsonToMap(string)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
//转换为Slice
|
||||||
|
func StrToSlice(string string) Slice {
|
||||||
|
|
||||||
|
data := ObjToSlice(string)
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
//字符串数组: a1,a2,a3转["a1","a2","a3"]
|
||||||
|
func StrArrayToJsonStr(a string) string {
|
||||||
|
|
||||||
|
if len(a) > 2 {
|
||||||
|
if a[0] == ',' {
|
||||||
|
a = Substr(a, 1, len(a)-1)
|
||||||
|
}
|
||||||
|
if a[len(a)-1] == ',' {
|
||||||
|
a = Substr(a, 0, len(a)-1)
|
||||||
|
}
|
||||||
|
//a = strings.Replace(a, ",", `,`, -1)
|
||||||
|
a = `[` + a + `]`
|
||||||
|
} else {
|
||||||
|
a = "[]"
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
//字符串数组: a1,a2,a3转["a1","a2","a3"]
|
||||||
|
func JsonStrToStrArray(a string) string {
|
||||||
|
//a = strings.Replace(a, `"`, "", -1)
|
||||||
|
if len(a) != 0 {
|
||||||
|
a = Substr(a, 1, len(a)-2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "," + a + ","
|
||||||
|
}
|
||||||
|
|
||||||
|
//字符串转int
|
||||||
|
func StrToInt(s string) (int, error) {
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
return i, err
|
||||||
|
|
||||||
|
}
|
||||||
104
vendor/code.hoteas.com/golang/hotime/common/slice.go
generated
vendored
Normal file
104
vendor/code.hoteas.com/golang/hotime/common/slice.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Slice []interface{}
|
||||||
|
|
||||||
|
// GetString 获取string
|
||||||
|
func (that Slice) GetString(key int, err ...*Error) string {
|
||||||
|
if len(err) != 0 {
|
||||||
|
err[0].SetError(nil)
|
||||||
|
}
|
||||||
|
return ObjToStr((that)[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) GetTime(key int, err ...*Error) time.Time {
|
||||||
|
|
||||||
|
v := ObjToTime((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt 获取Int
|
||||||
|
func (that Slice) GetInt(key int, err ...*Error) int {
|
||||||
|
v := ObjToInt((that)[key], err...)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64 获取Int
|
||||||
|
func (that Slice) GetInt64(key int, err ...*Error) int64 {
|
||||||
|
v := ObjToInt64((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCeilInt64 获取向上取整Int64
|
||||||
|
func (that Slice) GetCeilInt64(key int, err ...*Error) int64 {
|
||||||
|
v := ObjToCeilInt64((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCeilInt 获取向上取整Int
|
||||||
|
func (that Slice) GetCeilInt(key int, err ...*Error) int {
|
||||||
|
v := ObjToCeilInt((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCeilFloat64 获取向上取整float64
|
||||||
|
func (that Slice) GetCeilFloat64(key int, err ...*Error) float64 {
|
||||||
|
v := ObjToCeilFloat64((that)[key], err...)
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFloat64 获取Float64
|
||||||
|
func (that Slice) GetFloat64(key int, err ...*Error) float64 {
|
||||||
|
v := ObjToFloat64((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) GetSlice(key int, err ...*Error) Slice {
|
||||||
|
v := ObjToSlice((that)[key], err...)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) GetBool(key int, err ...*Error) bool {
|
||||||
|
|
||||||
|
//var v Slice
|
||||||
|
v := ObjToBool((that)[key], err...)
|
||||||
|
|
||||||
|
return v
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) GetMap(key int, err ...*Error) Map {
|
||||||
|
//var v Map
|
||||||
|
v := ObjToMap((that)[key], err...)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) Get(key int, err ...*Error) interface{} {
|
||||||
|
|
||||||
|
if key < len(that) {
|
||||||
|
return that[key]
|
||||||
|
}
|
||||||
|
e := errors.New("没有存储key及对应的数据")
|
||||||
|
if len(err) != 0 {
|
||||||
|
err[0].SetError(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) Put(key int, value interface{}) {
|
||||||
|
that[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that Slice) ToJsonString() string {
|
||||||
|
return ObjToStr(that)
|
||||||
|
}
|
||||||
4
vendor/code.hoteas.com/golang/hotime/const.go
generated
vendored
Normal file
4
vendor/code.hoteas.com/golang/hotime/const.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
// HEAD_SESSION_ADD session储存头
|
||||||
|
const HEAD_SESSION_ADD = "session#"
|
||||||
104
vendor/code.hoteas.com/golang/hotime/context.go
generated
vendored
Normal file
104
vendor/code.hoteas.com/golang/hotime/context.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
. "code.hoteas.com/golang/hotime/db"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
*Application
|
||||||
|
Resp http.ResponseWriter
|
||||||
|
Req *http.Request
|
||||||
|
Log Map //日志有则创建
|
||||||
|
RouterString []string
|
||||||
|
Config Map
|
||||||
|
Db *HoTimeDB
|
||||||
|
RespData Map
|
||||||
|
RespFunc func()
|
||||||
|
//CacheIns
|
||||||
|
SessionIns
|
||||||
|
DataSize int
|
||||||
|
HandlerStr string //复写请求url
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mtd 唯一标志
|
||||||
|
func (that *Context) Mtd(router [3]string) Map {
|
||||||
|
that.Application.Router[router[0]][router[1]][router[2]](that)
|
||||||
|
d := that.RespData
|
||||||
|
that.RespData = nil
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
//打印
|
||||||
|
func (that *Context) Display(statu int, data interface{}) {
|
||||||
|
|
||||||
|
resp := Map{"status": statu}
|
||||||
|
if statu != 0 {
|
||||||
|
temp := Map{}
|
||||||
|
|
||||||
|
tpe := that.Config.GetMap("error").GetString(ObjToStr(statu))
|
||||||
|
if tpe == "" {
|
||||||
|
//logFmt(errors.New("找不到对应的错误码"), 2, LOG_WARN)
|
||||||
|
}
|
||||||
|
|
||||||
|
temp["type"] = tpe
|
||||||
|
temp["msg"] = data
|
||||||
|
resp["result"] = temp
|
||||||
|
//兼容android等需要json转对象的服务
|
||||||
|
resp["error"] = temp
|
||||||
|
} else {
|
||||||
|
resp["result"] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
that.RespData = resp
|
||||||
|
|
||||||
|
//that.Data=d;
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *Context) View() {
|
||||||
|
if that.RespFunc != nil {
|
||||||
|
that.RespFunc()
|
||||||
|
}
|
||||||
|
if that.RespData == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//创建日志
|
||||||
|
if that.Log != nil {
|
||||||
|
that.Log["time"] = time.Now().Format("2006-01-02 15:04")
|
||||||
|
if that.Session("admin_id").Data != nil {
|
||||||
|
that.Log["admin_id"] = that.Session("admin_id").ToCeilInt()
|
||||||
|
}
|
||||||
|
if that.Session("user_id").Data != nil {
|
||||||
|
that.Log["user_id"] = that.Session("user_id").ToCeilInt()
|
||||||
|
}
|
||||||
|
//负载均衡优化
|
||||||
|
ipStr := ""
|
||||||
|
if that.Req.Header.Get("X-Forwarded-For") != "" {
|
||||||
|
ipStr = that.Req.Header.Get("X-Forwarded-For")
|
||||||
|
} else if that.Req.Header.Get("X-Real-IP") != "" {
|
||||||
|
ipStr = that.Req.Header.Get("X-Real-IP")
|
||||||
|
}
|
||||||
|
//负载均衡优化
|
||||||
|
if ipStr == "" {
|
||||||
|
//RemoteAddr := that.Req.RemoteAddr
|
||||||
|
ipStr = Substr(that.Req.RemoteAddr, 0, strings.Index(that.Req.RemoteAddr, ":"))
|
||||||
|
}
|
||||||
|
that.Log["ip"] = ipStr
|
||||||
|
that.Db.Insert("logs", that.Log)
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := json.Marshal(that.RespData)
|
||||||
|
if err != nil {
|
||||||
|
that.Display(1, err.Error())
|
||||||
|
that.View()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
that.DataSize = len(d)
|
||||||
|
that.RespData = nil
|
||||||
|
that.Resp.Write(d)
|
||||||
|
return
|
||||||
|
}
|
||||||
1372
vendor/code.hoteas.com/golang/hotime/db/hotimedb.go
generated
vendored
Normal file
1372
vendor/code.hoteas.com/golang/hotime/db/hotimedb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
142
vendor/code.hoteas.com/golang/hotime/dri/aliyun/company.go
generated
vendored
Normal file
142
vendor/code.hoteas.com/golang/hotime/dri/aliyun/company.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package aliyun
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
//"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type company struct {
|
||||||
|
ApiCode string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Company = company{}
|
||||||
|
|
||||||
|
func (that *company) Init(apiCode string) {
|
||||||
|
//"06c6a07e89dd45c88de040ee1489eef7"
|
||||||
|
that.ApiCode = apiCode
|
||||||
|
that.Url = "http://api.81api.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyOtherAll 获取企业基础信息
|
||||||
|
func (that *company) GetCompanyOtherAll(name string) Map {
|
||||||
|
|
||||||
|
res := Map{}
|
||||||
|
data, e := that.GetCompanyPatentsInfo(name) //获取专利信息
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
} else {
|
||||||
|
res["PatentsInfo"] = data.GetMap("data")
|
||||||
|
}
|
||||||
|
data, e = that.GetCompanyOtherCopyrightsInfo(name) //获取其他专利
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
} else {
|
||||||
|
res["OtherCopyrightsInfo"] = data.GetMap("data")
|
||||||
|
}
|
||||||
|
data, e = that.GetCompanyTrademarksInfo(name) //获取商标
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
} else {
|
||||||
|
res["TrademarksInfo"] = data.GetMap("data")
|
||||||
|
}
|
||||||
|
data, e = that.GetCompanySoftwareCopyrightsInfo(name) //获取软著
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
} else {
|
||||||
|
res["SoftwareCopyrightsInfo"] = data.GetMap("data")
|
||||||
|
}
|
||||||
|
data, e = that.GetCompanyProfileTags(name) //获取大数据标签
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e)
|
||||||
|
} else {
|
||||||
|
res["ProfileTags"] = data.GetSlice("data")
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyBaseInfo 获取企业基础信息
|
||||||
|
func (that *company) GetCompanyList(name string) (Map, error) {
|
||||||
|
url := "/fuzzyQueryCompanyInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyBaseInfo 获取企业基础信息
|
||||||
|
func (that *company) GetCompanyBaseInfo(name string) (Map, error) {
|
||||||
|
url := "/getCompanyBaseInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyPatentsInfo 获取专利信息
|
||||||
|
func (that *company) GetCompanyPatentsInfo(name string) (Map, error) {
|
||||||
|
url := "/getCompanyPatentsInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyTrademarksInfo 获取商标信息
|
||||||
|
func (that *company) GetCompanyTrademarksInfo(name string) (Map, error) {
|
||||||
|
url := "/getCompanyTrademarksInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanySoftwareCopyrightsInfo 获取软著信息
|
||||||
|
func (that *company) GetCompanySoftwareCopyrightsInfo(name string) (Map, error) {
|
||||||
|
url := "/getCompanySoftwareCopyrightsInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyOtherCopyrightsInfo 获取其他著作信息
|
||||||
|
func (that *company) GetCompanyOtherCopyrightsInfo(name string) (Map, error) {
|
||||||
|
url := "/getCompanyOtherCopyrightsInfo/"
|
||||||
|
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCompanyProfileTags 获取大数据标签
|
||||||
|
func (that *company) GetCompanyProfileTags(name string) (Map, error) {
|
||||||
|
url := "/getCompanyProfileTags/"
|
||||||
|
body, err := that.basePost(url, name)
|
||||||
|
return ObjToMap(body), err
|
||||||
|
}
|
||||||
|
func (that *company) basePost(url string, name string) (string, error) {
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
reqest, err := http.NewRequest("GET", that.Url+url+name+"/?isRaiseErrorCode=1", nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Fatal error ", err.Error())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqest.Header.Add("Authorization", "APPCODE "+that.ApiCode)
|
||||||
|
response, err := client.Do(reqest)
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Fatal error ", err.Error())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
res := string(body)
|
||||||
|
fmt.Println(res)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
54
vendor/code.hoteas.com/golang/hotime/dri/baidu/map.go
generated
vendored
Normal file
54
vendor/code.hoteas.com/golang/hotime/dri/baidu/map.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package baidu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baiduMap struct {
|
||||||
|
Ak string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
var BaiDuMap = baiduMap{}
|
||||||
|
|
||||||
|
func (that *baiduMap) Init(Ak string) {
|
||||||
|
//"ak=ZeT902EZvVgIoGVWEFK3osUm"
|
||||||
|
that.Ak = Ak
|
||||||
|
that.Url = "https://api.map.baidu.com/place/v2/suggestion?output=json" + "&ak=" + Ak
|
||||||
|
//query
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPosition 获取定位列表
|
||||||
|
func (that *baiduMap) GetPosition(name string, region string) (string, error) {
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
if region == "" {
|
||||||
|
region = "全国"
|
||||||
|
}
|
||||||
|
reqest, err := http.NewRequest("GET", that.Url+"&query="+url.PathEscape(name)+"®ion="+url.PathEscape(region), nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Fatal error ", err.Error())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
response, err := client.Do(reqest)
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Fatal error ", err.Error())
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println(string(body))
|
||||||
|
|
||||||
|
return string(body), err
|
||||||
|
|
||||||
|
}
|
||||||
93
vendor/code.hoteas.com/golang/hotime/dri/ddsms/ddsms.go
generated
vendored
Normal file
93
vendor/code.hoteas.com/golang/hotime/dri/ddsms/ddsms.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package ddsms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
//"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dingdongyun struct {
|
||||||
|
ApiKey string
|
||||||
|
YzmUrl string
|
||||||
|
TzUrl string
|
||||||
|
}
|
||||||
|
|
||||||
|
var DDY = dingdongyun{}
|
||||||
|
|
||||||
|
func (that *dingdongyun) Init(apikey string) {
|
||||||
|
that.ApiKey = apikey
|
||||||
|
that.YzmUrl = "https://api.dingdongcloud.com/v2/sms/captcha/send.json"
|
||||||
|
that.TzUrl = "https://api.dingdongcloud.com/v2/sms/notice/send.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendYZM 发送短信验证码 code验证码如:123456 返回true表示发送成功flase表示发送失败
|
||||||
|
func (that *dingdongyun) SendYZM(umoblie string, tpt string, data map[string]string) (bool, error) {
|
||||||
|
for k, v := range data {
|
||||||
|
tpt = strings.Replace(tpt, "{"+k+"}", v, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return that.send(that.YzmUrl, umoblie, tpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTz 发送通知
|
||||||
|
func (that *dingdongyun) SendTz(umoblie []string, tpt string, data map[string]string) (bool, error) {
|
||||||
|
for k, v := range data {
|
||||||
|
tpt = strings.Replace(tpt, "{"+k+"}", v, -1)
|
||||||
|
}
|
||||||
|
umobleStr := ""
|
||||||
|
for i, v := range umoblie {
|
||||||
|
if i == 0 {
|
||||||
|
umobleStr = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
umobleStr += "," + v
|
||||||
|
}
|
||||||
|
return that.send(that.TzUrl, umobleStr, tpt)
|
||||||
|
}
|
||||||
|
|
||||||
|
//发送短信
|
||||||
|
func (that *dingdongyun) send(mUrl string, umoblie string, content string) (bool, error) {
|
||||||
|
|
||||||
|
data_send_sms_yzm := url.Values{"apikey": {that.ApiKey}, "mobile": {umoblie}, "content": {content}}
|
||||||
|
res, err := that.httpsPostForm(mUrl, data_send_sms_yzm)
|
||||||
|
if err != nil && res == "" {
|
||||||
|
return false, errors.New("连接错误")
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg interface{}
|
||||||
|
err1 := json.Unmarshal([]byte(res), &msg)
|
||||||
|
if err1 != nil {
|
||||||
|
return false, errors.New("json解析错误")
|
||||||
|
}
|
||||||
|
//fmt.Println(msg)
|
||||||
|
resmsg := msg.(map[string]interface{})
|
||||||
|
rcode := int(resmsg["code"].(float64))
|
||||||
|
result := resmsg["msg"].(string)
|
||||||
|
if rcode != 1 {
|
||||||
|
return false, errors.New(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//调用url发送短信的连接
|
||||||
|
func (that *dingdongyun) httpsPostForm(url string, data url.Values) (string, error) {
|
||||||
|
resp, err := http.PostForm(url, data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
|
|
||||||
|
}
|
||||||
40
vendor/code.hoteas.com/golang/hotime/dri/download/download.go
generated
vendored
Normal file
40
vendor/code.hoteas.com/golang/hotime/dri/download/download.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package download
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Down 下载文件
|
||||||
|
func Down(url, path, name string, e ...*Error) bool {
|
||||||
|
|
||||||
|
os.MkdirAll(path, os.ModeDir)
|
||||||
|
if Substr(path, len(path)-1, len(path)) != "/" {
|
||||||
|
path = path + "/"
|
||||||
|
}
|
||||||
|
out, err := os.Create(path + name)
|
||||||
|
|
||||||
|
if err != nil && len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil && len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
pix, err := ioutil.ReadAll(resp.Body)
|
||||||
|
_, err = io.Copy(out, bytes.NewReader(pix))
|
||||||
|
if err != nil && len(e) != 0 {
|
||||||
|
e[0].SetError(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
119
vendor/code.hoteas.com/golang/hotime/dri/tencent/company.go
generated
vendored
Normal file
119
vendor/code.hoteas.com/golang/hotime/dri/tencent/company.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package tencent
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
gourl "net/url"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type company struct {
|
||||||
|
secretId string
|
||||||
|
secretKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Company = company{}
|
||||||
|
|
||||||
|
func (that *company) Init(secretId, secretKey string) {
|
||||||
|
// 云市场分配的密钥Id
|
||||||
|
//secretId := "xxxx"
|
||||||
|
//// 云市场分配的密钥Key
|
||||||
|
//secretKey := "xxxx"
|
||||||
|
that.secretId = secretId
|
||||||
|
that.secretKey = secretKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *company) calcAuthorization(source string) (auth string, datetime string, err error) {
|
||||||
|
|
||||||
|
timeLocation, _ := time.LoadLocation("Etc/GMT")
|
||||||
|
datetime = time.Now().In(timeLocation).Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
||||||
|
signStr := fmt.Sprintf("x-date: %s\nx-source: %s", datetime, source)
|
||||||
|
|
||||||
|
// hmac-sha1
|
||||||
|
mac := hmac.New(sha1.New, []byte(that.secretKey))
|
||||||
|
mac.Write([]byte(signStr))
|
||||||
|
sign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||||
|
|
||||||
|
auth = fmt.Sprintf("hmac id=\"%s\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"%s\"",
|
||||||
|
that.secretId, sign)
|
||||||
|
|
||||||
|
return auth, datetime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *company) urlencode(params map[string]string) string {
|
||||||
|
var p = gourl.Values{}
|
||||||
|
for k, v := range params {
|
||||||
|
p.Add(k, v)
|
||||||
|
}
|
||||||
|
return p.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *company) GetCompany(name string) Map {
|
||||||
|
// 云市场分配的密钥Id
|
||||||
|
//secretId := "xxxx"
|
||||||
|
//// 云市场分配的密钥Key
|
||||||
|
//secretKey := "xxxx"
|
||||||
|
source := "market"
|
||||||
|
|
||||||
|
// 签名
|
||||||
|
auth, datetime, _ := that.calcAuthorization(source)
|
||||||
|
|
||||||
|
// 请求方法
|
||||||
|
method := "GET"
|
||||||
|
// 请求头
|
||||||
|
headers := map[string]string{"X-Source": source, "X-Date": datetime, "Authorization": auth}
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
queryParams := make(map[string]string)
|
||||||
|
queryParams["keyword"] = name
|
||||||
|
// body参数
|
||||||
|
bodyParams := make(map[string]string)
|
||||||
|
|
||||||
|
// url参数拼接
|
||||||
|
url := "https://service-3jnh3ku8-1256140209.gz.apigw.tencentcs.com/release/business4/geet"
|
||||||
|
if len(queryParams) > 0 {
|
||||||
|
url = fmt.Sprintf("%s?%s", url, that.urlencode(queryParams))
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMethods := map[string]bool{"POST": true, "PUT": true, "PATCH": true}
|
||||||
|
var body io.Reader = nil
|
||||||
|
if bodyMethods[method] {
|
||||||
|
body = strings.NewReader(that.urlencode(bodyParams))
|
||||||
|
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest(method, url, body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for k, v := range headers {
|
||||||
|
request.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
response, err := client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
bodyBytes, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res := string(bodyBytes)
|
||||||
|
fmt.Println(res)
|
||||||
|
return ObjToMap(res)
|
||||||
|
}
|
||||||
104
vendor/code.hoteas.com/golang/hotime/dri/tencent/tencent.go
generated
vendored
Normal file
104
vendor/code.hoteas.com/golang/hotime/dri/tencent/tencent.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package tencent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||||||
|
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||||
|
ocr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr/v20181119"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tencent struct {
|
||||||
|
secretId string
|
||||||
|
secretKey string
|
||||||
|
credential *common.Credential
|
||||||
|
}
|
||||||
|
|
||||||
|
var Tencent = tencent{}
|
||||||
|
|
||||||
|
func (that *tencent) Init(secretId, secretKey string) {
|
||||||
|
// 云市场分配的密钥Id
|
||||||
|
//secretId := "xxxx"
|
||||||
|
//// 云市场分配的密钥Key
|
||||||
|
//secretKey := "xxxx"
|
||||||
|
that.secretId = secretId
|
||||||
|
that.secretKey = secretKey
|
||||||
|
that.credential = common.NewCredential(
|
||||||
|
that.secretId,
|
||||||
|
that.secretKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *tencent) OCRCOMPANY(base64Str string) string {
|
||||||
|
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||||
|
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||||
|
|
||||||
|
request := ocr.NewBizLicenseOCRRequest()
|
||||||
|
|
||||||
|
//request.ImageUrl = common.StringPtr("https://img0.baidu.com/it/u=2041013181,3227632688&fm=26&fmt=auto")
|
||||||
|
request.ImageBase64 = common.StringPtr(base64Str)
|
||||||
|
|
||||||
|
response, err := client.BizLicenseOCR(request)
|
||||||
|
if _, ok := err.(*errors.TencentCloudSDKError); ok {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//fmt.Printf("%s", response.ToJsonString())
|
||||||
|
|
||||||
|
return response.ToJsonString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *tencent) OCR(base64Str string) string {
|
||||||
|
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||||
|
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||||
|
|
||||||
|
request := ocr.NewGeneralAccurateOCRRequest()
|
||||||
|
|
||||||
|
//request.ImageUrl = common.StringPtr("https://img0.baidu.com/it/u=2041013181,3227632688&fm=26&fmt=auto")
|
||||||
|
request.ImageBase64 = common.StringPtr(base64Str)
|
||||||
|
|
||||||
|
response, err := client.GeneralAccurateOCR(request)
|
||||||
|
if _, ok := err.(*errors.TencentCloudSDKError); ok {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//fmt.Printf("%s", response.ToJsonString())
|
||||||
|
|
||||||
|
return response.ToJsonString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *tencent) Qrcode(base64Str string) string {
|
||||||
|
|
||||||
|
cpf := profile.NewClientProfile()
|
||||||
|
cpf.HttpProfile.Endpoint = "ocr.tencentcloudapi.com"
|
||||||
|
client, _ := ocr.NewClient(that.credential, "ap-guangzhou", cpf)
|
||||||
|
|
||||||
|
request := ocr.NewQrcodeOCRRequest()
|
||||||
|
|
||||||
|
request.ImageBase64 = common.StringPtr(base64Str)
|
||||||
|
|
||||||
|
response, err := client.QrcodeOCR(request)
|
||||||
|
if _, ok := err.(*errors.TencentCloudSDKError); ok {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("An API error has returned: %s", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
//fmt.Printf("%s", response.ToJsonString())
|
||||||
|
|
||||||
|
return response.ToJsonString()
|
||||||
|
}
|
||||||
17
vendor/code.hoteas.com/golang/hotime/go.mod
generated
vendored
Normal file
17
vendor/code.hoteas.com/golang/hotime/go.mod
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module code.hoteas.com/golang/hotime
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/360EntSecGroup-Skylar/excelize v1.4.1
|
||||||
|
github.com/garyburd/redigo v1.6.3
|
||||||
|
github.com/go-pay/gopay v1.5.78
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.12
|
||||||
|
github.com/silenceper/wechat/v2 v2.1.2
|
||||||
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364
|
||||||
|
go.mongodb.org/mongo-driver v1.10.1
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||||
|
)
|
||||||
107
vendor/code.hoteas.com/golang/hotime/go.sum
generated
vendored
Normal file
107
vendor/code.hoteas.com/golang/hotime/go.sum
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
|
||||||
|
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
|
github.com/garyburd/redigo v1.6.3 h1:HCeeRluvAgMusMomi1+6Y5dmFOdYV/JzoRrrbFlkGIc=
|
||||||
|
github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
|
||||||
|
github.com/go-pay/gopay v1.5.78 h1:wIHp8g/jK0ik5bZo2MWt3jAQsktT3nkdXZxlRZvljko=
|
||||||
|
github.com/go-pay/gopay v1.5.78/go.mod h1:M6Nlk2VdZHCbWphOw3rtbnz4SiOk6Xvxg6mxwDfg+Ps=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.5 h1:nRAxCa+SVsyjSBrtZmG/cqb6VbTmuRzpg/PoTFlpumc=
|
||||||
|
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||||
|
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
|
||||||
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/silenceper/wechat/v2 v2.1.2 h1:+QfIMiYfwST2ZloTwmYp0O0p5Y1LYRZxfLWfMuSE30k=
|
||||||
|
github.com/silenceper/wechat/v2 v2.1.2/go.mod h1:0OprxYCCp2CZAKw06BBlnaczInTk2KxOLsKeiopshGg=
|
||||||
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364 h1:X1Jws4XqrTH+p7FBQ7BpjW4qFXObKHWm0/XhW/GvqRs=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.364/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364 h1:kbor60vo37v7Hu+i17gooox9Rw281fVHNna8zwtDG1w=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ocr v1.0.364/go.mod h1:LeIUBOLhc+Y5YCEpZrULPD9lgoXXV4/EmIcoEvmHz9c=
|
||||||
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
|
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||||
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
|
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
|
||||||
|
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
|
||||||
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||||
|
go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4=
|
||||||
|
go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
||||||
|
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
||||||
|
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
136
vendor/code.hoteas.com/golang/hotime/log/logrus.go
generated
vendored
Normal file
136
vendor/code.hoteas.com/golang/hotime/log/logrus.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetLog(path string, showCodeLine bool) *log.Logger {
|
||||||
|
|
||||||
|
hook := MyHook{
|
||||||
|
Field: "line",
|
||||||
|
Skip: 5,
|
||||||
|
Path: path, ShowCodeLine: showCodeLine,
|
||||||
|
}
|
||||||
|
|
||||||
|
loger := log.New()
|
||||||
|
loger.SetFormatter(&log.TextFormatter{})
|
||||||
|
loger.AddHook(&hook)
|
||||||
|
return loger
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyHook ...
|
||||||
|
type MyHook struct {
|
||||||
|
Path string //存储日志的位置
|
||||||
|
ShowCodeLine bool //输出代码文件名称和日志行
|
||||||
|
Field string
|
||||||
|
Skip int
|
||||||
|
levels []log.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels 只定义 error 和 panic 等级的日志,其他日志等级不会触发 hook
|
||||||
|
func (that *MyHook) Levels() []log.Level {
|
||||||
|
return log.AllLevels
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire 将异常日志写入到指定日志文件中
|
||||||
|
func (that *MyHook) Fire(entry *log.Entry) error {
|
||||||
|
if that.ShowCodeLine {
|
||||||
|
entry.Data[that.Field] = findCaller(that.Skip)
|
||||||
|
}
|
||||||
|
//不需要存储到文件
|
||||||
|
if that.Path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
//存储到文件
|
||||||
|
logFilePath := time.Now().Format(that.Path)
|
||||||
|
|
||||||
|
err := os.MkdirAll(filepath.Dir(logFilePath), os.ModeAppend)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//os.Create(logFilePath)
|
||||||
|
f, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bte, _ := entry.Bytes()
|
||||||
|
_, err = f.Write(bte)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对caller进行递归查询, 直到找到非logrus包产生的第一个调用.
|
||||||
|
// 因为filename我获取到了上层目录名, 因此所有logrus包的调用的文件名都是 logrus/...
|
||||||
|
// 因此通过排除logrus开头的文件名, 就可以排除所有logrus包的自己的函数调用
|
||||||
|
func findCaller(skip int) string {
|
||||||
|
file := ""
|
||||||
|
line := 0
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
file, line = getCaller(skip + i)
|
||||||
|
if !strings.HasPrefix(file, "logrus") {
|
||||||
|
j := 0
|
||||||
|
for true {
|
||||||
|
j++
|
||||||
|
if file == "common/error.go" {
|
||||||
|
file, line = getCaller(skip + i + j)
|
||||||
|
}
|
||||||
|
if file == "db/hotimedb.go" {
|
||||||
|
file, line = getCaller(skip + i + j)
|
||||||
|
}
|
||||||
|
if file == "code/makecode.go" {
|
||||||
|
file, line = getCaller(skip + i + j)
|
||||||
|
}
|
||||||
|
if strings.Index(file, "common/") == 0 {
|
||||||
|
file, line = getCaller(skip + i + j)
|
||||||
|
}
|
||||||
|
if strings.Contains(file, "application.go") {
|
||||||
|
file, line = getCaller(skip + i + j)
|
||||||
|
}
|
||||||
|
if j == 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%d", file, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里其实可以获取函数名称的: fnName := runtime.FuncForPC(pc).Name()
|
||||||
|
// 但是我觉得有 文件名和行号就够定位问题, 因此忽略了caller返回的第一个值:pc
|
||||||
|
// 在标准库log里面我们可以选择记录文件的全路径或者文件名, 但是在使用过程成并发最合适的,
|
||||||
|
// 因为文件的全路径往往很长, 而文件名在多个包中往往有重复, 因此这里选择多取一层, 取到文件所在的上层目录那层.
|
||||||
|
func getCaller(skip int) (string, int) {
|
||||||
|
_, file, line, ok := runtime.Caller(skip)
|
||||||
|
//fmt.Println(file)
|
||||||
|
//fmt.Println(line)
|
||||||
|
if !ok {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
n := 0
|
||||||
|
for i := len(file) - 1; i > 0; i-- {
|
||||||
|
if file[i] == '/' {
|
||||||
|
n++
|
||||||
|
if n >= 2 {
|
||||||
|
file = file[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file, line
|
||||||
|
}
|
||||||
543
vendor/code.hoteas.com/golang/hotime/mime.go
generated
vendored
Normal file
543
vendor/code.hoteas.com/golang/hotime/mime.go
generated
vendored
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
var MimeMaps = map[string]string{
|
||||||
|
".3dm": "x-world/x-3dmf",
|
||||||
|
".3dmf": "x-world/x-3dmf",
|
||||||
|
".7z": "application/x-7z-compressed",
|
||||||
|
".a": "application/octet-stream",
|
||||||
|
".aab": "application/x-authorware-bin",
|
||||||
|
".aam": "application/x-authorware-map",
|
||||||
|
".aas": "application/x-authorware-seg",
|
||||||
|
".abc": "text/vndabc",
|
||||||
|
".ace": "application/x-ace-compressed",
|
||||||
|
".acgi": "text/html",
|
||||||
|
".m3u8": "audio/mpegurl",
|
||||||
|
".afl": "video/animaflex",
|
||||||
|
".ai": "application/postscript",
|
||||||
|
".aif": "audio/aiff",
|
||||||
|
".aifc": "audio/aiff",
|
||||||
|
".aiff": "audio/aiff",
|
||||||
|
".aim": "application/x-aim",
|
||||||
|
".aip": "text/x-audiosoft-intra",
|
||||||
|
".alz": "application/x-alz-compressed",
|
||||||
|
".ani": "application/x-navi-animation",
|
||||||
|
".aos": "application/x-nokia-9000-communicator-add-on-software",
|
||||||
|
".aps": "application/mime",
|
||||||
|
".apk": "application/vnd.android.package-archive",
|
||||||
|
".arc": "application/x-arc-compressed",
|
||||||
|
".arj": "application/arj",
|
||||||
|
".art": "image/x-jg",
|
||||||
|
".asf": "video/x-ms-asf",
|
||||||
|
".asm": "text/x-asm",
|
||||||
|
".asp": "text/asp",
|
||||||
|
".asx": "application/x-mplayer2",
|
||||||
|
".au": "audio/basic",
|
||||||
|
".avi": "video/x-msvideo",
|
||||||
|
".avs": "video/avs-video",
|
||||||
|
".bcpio": "application/x-bcpio",
|
||||||
|
".bin": "application/mac-binary",
|
||||||
|
".bmp": "image/bmp",
|
||||||
|
".boo": "application/book",
|
||||||
|
".book": "application/book",
|
||||||
|
".boz": "application/x-bzip2",
|
||||||
|
".bsh": "application/x-bsh",
|
||||||
|
".bz2": "application/x-bzip2",
|
||||||
|
".bz": "application/x-bzip",
|
||||||
|
".c++": "text/plain",
|
||||||
|
".c": "text/x-c",
|
||||||
|
".cab": "application/vnd.ms-cab-compressed",
|
||||||
|
".cat": "application/vndms-pkiseccat",
|
||||||
|
".cc": "text/x-c",
|
||||||
|
".ccad": "application/clariscad",
|
||||||
|
".cco": "application/x-cocoa",
|
||||||
|
".cdf": "application/cdf",
|
||||||
|
".cer": "application/pkix-cert",
|
||||||
|
".cha": "application/x-chat",
|
||||||
|
".chat": "application/x-chat",
|
||||||
|
".chrt": "application/vnd.kde.kchart",
|
||||||
|
".class": "application/java",
|
||||||
|
".com": "text/plain",
|
||||||
|
".conf": "text/plain",
|
||||||
|
".cpio": "application/x-cpio",
|
||||||
|
".cpp": "text/x-c",
|
||||||
|
".cpt": "application/mac-compactpro",
|
||||||
|
".crl": "application/pkcs-crl",
|
||||||
|
".crt": "application/pkix-cert",
|
||||||
|
".crx": "application/x-chrome-extension",
|
||||||
|
".csh": "text/x-scriptcsh",
|
||||||
|
".css": "text/css;charset=utf-8",
|
||||||
|
".csv": "text/csv",
|
||||||
|
".cxx": "text/plain",
|
||||||
|
".dar": "application/x-dar",
|
||||||
|
".dcr": "application/x-director",
|
||||||
|
".deb": "application/x-debian-package",
|
||||||
|
".deepv": "application/x-deepv",
|
||||||
|
".def": "text/plain",
|
||||||
|
".der": "application/x-x509-ca-cert",
|
||||||
|
".dif": "video/x-dv",
|
||||||
|
".dir": "application/x-director",
|
||||||
|
".divx": "video/divx",
|
||||||
|
".dl": "video/dl",
|
||||||
|
".dmg": "application/x-apple-diskimage",
|
||||||
|
".doc": "application/msword",
|
||||||
|
".dot": "application/msword",
|
||||||
|
".dp": "application/commonground",
|
||||||
|
".drw": "application/drafting",
|
||||||
|
".dump": "application/octet-stream",
|
||||||
|
".dv": "video/x-dv",
|
||||||
|
".dvi": "application/x-dvi",
|
||||||
|
".dwf": "drawing/x-dwf=(old)",
|
||||||
|
".dwg": "application/acad",
|
||||||
|
".dxf": "application/dxf",
|
||||||
|
".dxr": "application/x-director",
|
||||||
|
".el": "text/x-scriptelisp",
|
||||||
|
".elc": "application/x-bytecodeelisp=(compiled=elisp)",
|
||||||
|
".eml": "message/rfc822",
|
||||||
|
".env": "application/x-envoy",
|
||||||
|
".eps": "application/postscript",
|
||||||
|
".es": "application/x-esrehber",
|
||||||
|
".etx": "text/x-setext",
|
||||||
|
".evy": "application/envoy",
|
||||||
|
".exe": "application/octet-stream",
|
||||||
|
".f77": "text/x-fortran",
|
||||||
|
".f90": "text/x-fortran",
|
||||||
|
".f": "text/x-fortran",
|
||||||
|
".fdf": "application/vndfdf",
|
||||||
|
".fif": "application/fractals",
|
||||||
|
".fli": "video/fli",
|
||||||
|
".flo": "image/florian",
|
||||||
|
".flv": "video/x-flv",
|
||||||
|
".flx": "text/vndfmiflexstor",
|
||||||
|
".fmf": "video/x-atomic3d-feature",
|
||||||
|
".for": "text/x-fortran",
|
||||||
|
".fpx": "image/vndfpx",
|
||||||
|
".frl": "application/freeloader",
|
||||||
|
".funk": "audio/make",
|
||||||
|
".g3": "image/g3fax",
|
||||||
|
".g": "text/plain",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".gl": "video/gl",
|
||||||
|
".gsd": "audio/x-gsm",
|
||||||
|
".gsm": "audio/x-gsm",
|
||||||
|
".gsp": "application/x-gsp",
|
||||||
|
".gss": "application/x-gss",
|
||||||
|
".gtar": "application/x-gtar",
|
||||||
|
".gz": "application/x-compressed",
|
||||||
|
".gzip": "application/x-gzip",
|
||||||
|
".h": "text/x-h",
|
||||||
|
".hdf": "application/x-hdf",
|
||||||
|
".help": "application/x-helpfile",
|
||||||
|
".hgl": "application/vndhp-hpgl",
|
||||||
|
".hh": "text/x-h",
|
||||||
|
".hlb": "text/x-script",
|
||||||
|
".hlp": "application/hlp",
|
||||||
|
".hpg": "application/vndhp-hpgl",
|
||||||
|
".hpgl": "application/vndhp-hpgl",
|
||||||
|
".hqx": "application/binhex",
|
||||||
|
".hta": "application/hta",
|
||||||
|
".htc": "text/x-component",
|
||||||
|
".htm": "text/html;charset=utf-8",
|
||||||
|
".html": "text/html;charset=utf-8",
|
||||||
|
".htmls": "text/html",
|
||||||
|
".htt": "text/webviewhtml",
|
||||||
|
".htx": "text/html",
|
||||||
|
".ice": "x-conference/x-cooltalk",
|
||||||
|
".ico": "image/x-icon",
|
||||||
|
".ics": "text/calendar",
|
||||||
|
".icz": "text/calendar",
|
||||||
|
".idc": "text/plain",
|
||||||
|
".ief": "image/ief",
|
||||||
|
".iefs": "image/ief",
|
||||||
|
".iges": "application/iges",
|
||||||
|
".igs": "application/iges",
|
||||||
|
".ima": "application/x-ima",
|
||||||
|
".imap": "application/x-httpd-imap",
|
||||||
|
".inf": "application/inf",
|
||||||
|
".ins": "application/x-internett-signup",
|
||||||
|
".ip": "application/x-ip2",
|
||||||
|
".isu": "video/x-isvideo",
|
||||||
|
".it": "audio/it",
|
||||||
|
".iv": "application/x-inventor",
|
||||||
|
".ivr": "i-world/i-vrml",
|
||||||
|
".ivy": "application/x-livescreen",
|
||||||
|
".jam": "audio/x-jam",
|
||||||
|
".jav": "text/x-java-source",
|
||||||
|
".java": "text/x-java-source",
|
||||||
|
".jcm": "application/x-java-commerce",
|
||||||
|
".jfif-tbnl": "image/jpeg",
|
||||||
|
".jfif": "image/jpeg",
|
||||||
|
".jnlp": "application/x-java-jnlp-file",
|
||||||
|
".jpe": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jps": "image/x-jps",
|
||||||
|
".js": "application/javascript;charset=utf-8",
|
||||||
|
".json": "application/json",
|
||||||
|
".jut": "image/jutvision",
|
||||||
|
".kar": "audio/midi",
|
||||||
|
".karbon": "application/vnd.kde.karbon",
|
||||||
|
".kfo": "application/vnd.kde.kformula",
|
||||||
|
".flw": "application/vnd.kde.kivio",
|
||||||
|
".kml": "application/vnd.google-earth.kml+xml",
|
||||||
|
".kmz": "application/vnd.google-earth.kmz",
|
||||||
|
".kon": "application/vnd.kde.kontour",
|
||||||
|
".kpr": "application/vnd.kde.kpresenter",
|
||||||
|
".kpt": "application/vnd.kde.kpresenter",
|
||||||
|
".ksp": "application/vnd.kde.kspread",
|
||||||
|
".kwd": "application/vnd.kde.kword",
|
||||||
|
".kwt": "application/vnd.kde.kword",
|
||||||
|
".ksh": "text/x-scriptksh",
|
||||||
|
".la": "audio/nspaudio",
|
||||||
|
".lam": "audio/x-liveaudio",
|
||||||
|
".latex": "application/x-latex",
|
||||||
|
".lha": "application/lha",
|
||||||
|
".lhx": "application/octet-stream",
|
||||||
|
".list": "text/plain",
|
||||||
|
".lma": "audio/nspaudio",
|
||||||
|
".log": "text/plain",
|
||||||
|
".lsp": "text/x-scriptlisp",
|
||||||
|
".lst": "text/plain",
|
||||||
|
".lsx": "text/x-la-asf",
|
||||||
|
".ltx": "application/x-latex",
|
||||||
|
".lzh": "application/octet-stream",
|
||||||
|
".lzx": "application/lzx",
|
||||||
|
".m1v": "video/mpeg",
|
||||||
|
".m2a": "audio/mpeg",
|
||||||
|
".m2v": "video/mpeg",
|
||||||
|
".m3u": "audio/x-mpegurl",
|
||||||
|
".m": "text/x-m",
|
||||||
|
".man": "application/x-troff-man",
|
||||||
|
".manifest": "text/cache-manifest",
|
||||||
|
".map": "application/x-navimap",
|
||||||
|
".mar": "text/plain",
|
||||||
|
".mbd": "application/mbedlet",
|
||||||
|
".mc$": "application/x-magic-cap-package-10",
|
||||||
|
".mcd": "application/mcad",
|
||||||
|
".mcf": "text/mcf",
|
||||||
|
".mcp": "application/netmc",
|
||||||
|
".me": "application/x-troff-me",
|
||||||
|
".mht": "message/rfc822",
|
||||||
|
".mhtml": "message/rfc822",
|
||||||
|
".mid": "application/x-midi",
|
||||||
|
".midi": "application/x-midi",
|
||||||
|
".mif": "application/x-frame",
|
||||||
|
".mime": "message/rfc822",
|
||||||
|
".mjf": "audio/x-vndaudioexplosionmjuicemediafile",
|
||||||
|
".mjpg": "video/x-motion-jpeg",
|
||||||
|
".mm": "application/base64",
|
||||||
|
".mme": "application/base64",
|
||||||
|
".mod": "audio/mod",
|
||||||
|
".moov": "video/quicktime",
|
||||||
|
".mov": "video/quicktime",
|
||||||
|
".movie": "video/x-sgi-movie",
|
||||||
|
".mp2": "audio/mpeg",
|
||||||
|
".mp3": "audio/mpeg3",
|
||||||
|
".mp4": "video/mp4",
|
||||||
|
".mpa": "audio/mpeg",
|
||||||
|
".mpc": "application/x-project",
|
||||||
|
".mpe": "video/mpeg",
|
||||||
|
".mpeg": "video/mpeg",
|
||||||
|
".mpg": "video/mpeg",
|
||||||
|
".mpga": "audio/mpeg",
|
||||||
|
".mpp": "application/vndms-project",
|
||||||
|
".mpt": "application/x-project",
|
||||||
|
".mpv": "application/x-project",
|
||||||
|
".mpx": "application/x-project",
|
||||||
|
".mrc": "application/marc",
|
||||||
|
".ms": "application/x-troff-ms",
|
||||||
|
".mv": "video/x-sgi-movie",
|
||||||
|
".my": "audio/make",
|
||||||
|
".mzz": "application/x-vndaudioexplosionmzz",
|
||||||
|
".nap": "image/naplps",
|
||||||
|
".naplps": "image/naplps",
|
||||||
|
".nc": "application/x-netcdf",
|
||||||
|
".ncm": "application/vndnokiaconfiguration-message",
|
||||||
|
".nif": "image/x-niff",
|
||||||
|
".niff": "image/x-niff",
|
||||||
|
".nix": "application/x-mix-transfer",
|
||||||
|
".nsc": "application/x-conference",
|
||||||
|
".nvd": "application/x-navidoc",
|
||||||
|
".o": "application/octet-stream",
|
||||||
|
".oda": "application/oda",
|
||||||
|
".odb": "application/vnd.oasis.opendocument.database",
|
||||||
|
".odc": "application/vnd.oasis.opendocument.chart",
|
||||||
|
".odf": "application/vnd.oasis.opendocument.formula",
|
||||||
|
".odg": "application/vnd.oasis.opendocument.graphics",
|
||||||
|
".odi": "application/vnd.oasis.opendocument.image",
|
||||||
|
".odm": "application/vnd.oasis.opendocument.text-master",
|
||||||
|
".odp": "application/vnd.oasis.opendocument.presentation",
|
||||||
|
".ods": "application/vnd.oasis.opendocument.spreadsheet",
|
||||||
|
".odt": "application/vnd.oasis.opendocument.text",
|
||||||
|
".oga": "audio/ogg",
|
||||||
|
".ogg": "audio/ogg",
|
||||||
|
".ogv": "video/ogg",
|
||||||
|
".omc": "application/x-omc",
|
||||||
|
".omcd": "application/x-omcdatamaker",
|
||||||
|
".omcr": "application/x-omcregerator",
|
||||||
|
".otc": "application/vnd.oasis.opendocument.chart-template",
|
||||||
|
".otf": "application/vnd.oasis.opendocument.formula-template",
|
||||||
|
".otg": "application/vnd.oasis.opendocument.graphics-template",
|
||||||
|
".oth": "application/vnd.oasis.opendocument.text-web",
|
||||||
|
".oti": "application/vnd.oasis.opendocument.image-template",
|
||||||
|
".otm": "application/vnd.oasis.opendocument.text-master",
|
||||||
|
".otp": "application/vnd.oasis.opendocument.presentation-template",
|
||||||
|
".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
|
||||||
|
".ott": "application/vnd.oasis.opendocument.text-template",
|
||||||
|
".p10": "application/pkcs10",
|
||||||
|
".p12": "application/pkcs-12",
|
||||||
|
".p7a": "application/x-pkcs7-signature",
|
||||||
|
".p7c": "application/pkcs7-mime",
|
||||||
|
".p7m": "application/pkcs7-mime",
|
||||||
|
".p7r": "application/x-pkcs7-certreqresp",
|
||||||
|
".p7s": "application/pkcs7-signature",
|
||||||
|
".p": "text/x-pascal",
|
||||||
|
".part": "application/pro_eng",
|
||||||
|
".pas": "text/pascal",
|
||||||
|
".pbm": "image/x-portable-bitmap",
|
||||||
|
".pcl": "application/vndhp-pcl",
|
||||||
|
".pct": "image/x-pict",
|
||||||
|
".pcx": "image/x-pcx",
|
||||||
|
".pdb": "chemical/x-pdb",
|
||||||
|
".pdf": "application/pdf",
|
||||||
|
".pfunk": "audio/make",
|
||||||
|
".pgm": "image/x-portable-graymap",
|
||||||
|
".pic": "image/pict",
|
||||||
|
".pict": "image/pict",
|
||||||
|
".pkg": "application/x-newton-compatible-pkg",
|
||||||
|
".pko": "application/vndms-pkipko",
|
||||||
|
".pl": "text/x-scriptperl",
|
||||||
|
".plx": "application/x-pixclscript",
|
||||||
|
".pm4": "application/x-pagemaker",
|
||||||
|
".pm5": "application/x-pagemaker",
|
||||||
|
".pm": "text/x-scriptperl-module",
|
||||||
|
".png": "image/png",
|
||||||
|
".pnm": "application/x-portable-anymap",
|
||||||
|
".pot": "application/mspowerpoint",
|
||||||
|
".pov": "model/x-pov",
|
||||||
|
".ppa": "application/vndms-powerpoint",
|
||||||
|
".ppm": "image/x-portable-pixmap",
|
||||||
|
".pps": "application/mspowerpoint",
|
||||||
|
".ppt": "application/mspowerpoint",
|
||||||
|
".ppz": "application/mspowerpoint",
|
||||||
|
".pre": "application/x-freelance",
|
||||||
|
".prt": "application/pro_eng",
|
||||||
|
".ps": "application/postscript",
|
||||||
|
".psd": "application/octet-stream",
|
||||||
|
".pvu": "paleovu/x-pv",
|
||||||
|
".pwz": "application/vndms-powerpoint",
|
||||||
|
".py": "text/x-scriptphyton",
|
||||||
|
".pyc": "applicaiton/x-bytecodepython",
|
||||||
|
".qcp": "audio/vndqcelp",
|
||||||
|
".qd3": "x-world/x-3dmf",
|
||||||
|
".qd3d": "x-world/x-3dmf",
|
||||||
|
".qif": "image/x-quicktime",
|
||||||
|
".qt": "video/quicktime",
|
||||||
|
".qtc": "video/x-qtc",
|
||||||
|
".qti": "image/x-quicktime",
|
||||||
|
".qtif": "image/x-quicktime",
|
||||||
|
".ra": "audio/x-pn-realaudio",
|
||||||
|
".ram": "audio/x-pn-realaudio",
|
||||||
|
".rar": "application/x-rar-compressed",
|
||||||
|
".ras": "application/x-cmu-raster",
|
||||||
|
".rast": "image/cmu-raster",
|
||||||
|
".rexx": "text/x-scriptrexx",
|
||||||
|
".rf": "image/vndrn-realflash",
|
||||||
|
".rgb": "image/x-rgb",
|
||||||
|
".rm": "application/vndrn-realmedia",
|
||||||
|
".rmi": "audio/mid",
|
||||||
|
".rmm": "audio/x-pn-realaudio",
|
||||||
|
".rmp": "audio/x-pn-realaudio",
|
||||||
|
".rng": "application/ringing-tones",
|
||||||
|
".rnx": "application/vndrn-realplayer",
|
||||||
|
".roff": "application/x-troff",
|
||||||
|
".rp": "image/vndrn-realpix",
|
||||||
|
".rpm": "audio/x-pn-realaudio-plugin",
|
||||||
|
".rt": "text/vndrn-realtext",
|
||||||
|
".rtf": "text/richtext",
|
||||||
|
".rtx": "text/richtext",
|
||||||
|
".rv": "video/vndrn-realvideo",
|
||||||
|
".s": "text/x-asm",
|
||||||
|
".s3m": "audio/s3m",
|
||||||
|
".s7z": "application/x-7z-compressed",
|
||||||
|
".saveme": "application/octet-stream",
|
||||||
|
".sbk": "application/x-tbook",
|
||||||
|
".scm": "text/x-scriptscheme",
|
||||||
|
".sdml": "text/plain",
|
||||||
|
".sdp": "application/sdp",
|
||||||
|
".sdr": "application/sounder",
|
||||||
|
".sea": "application/sea",
|
||||||
|
".set": "application/set",
|
||||||
|
".sgm": "text/x-sgml",
|
||||||
|
".sgml": "text/x-sgml",
|
||||||
|
".sh": "text/x-scriptsh",
|
||||||
|
".shar": "application/x-bsh",
|
||||||
|
".shtml": "text/x-server-parsed-html",
|
||||||
|
".sid": "audio/x-psid",
|
||||||
|
".skd": "application/x-koan",
|
||||||
|
".skm": "application/x-koan",
|
||||||
|
".skp": "application/x-koan",
|
||||||
|
".skt": "application/x-koan",
|
||||||
|
".sit": "application/x-stuffit",
|
||||||
|
".sitx": "application/x-stuffitx",
|
||||||
|
".sl": "application/x-seelogo",
|
||||||
|
".smi": "application/smil",
|
||||||
|
".smil": "application/smil",
|
||||||
|
".snd": "audio/basic",
|
||||||
|
".sol": "application/solids",
|
||||||
|
".spc": "text/x-speech",
|
||||||
|
".spl": "application/futuresplash",
|
||||||
|
".spr": "application/x-sprite",
|
||||||
|
".sprite": "application/x-sprite",
|
||||||
|
".spx": "audio/ogg",
|
||||||
|
".src": "application/x-wais-source",
|
||||||
|
".ssi": "text/x-server-parsed-html",
|
||||||
|
".ssm": "application/streamingmedia",
|
||||||
|
".sst": "application/vndms-pkicertstore",
|
||||||
|
".step": "application/step",
|
||||||
|
".stl": "application/sla",
|
||||||
|
".stp": "application/step",
|
||||||
|
".sv4cpio": "application/x-sv4cpio",
|
||||||
|
".sv4crc": "application/x-sv4crc",
|
||||||
|
".svf": "image/vnddwg",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".svr": "application/x-world",
|
||||||
|
".swf": "application/x-shockwave-flash",
|
||||||
|
".t": "application/x-troff",
|
||||||
|
".talk": "text/x-speech",
|
||||||
|
".tar": "application/x-tar",
|
||||||
|
".tbk": "application/toolbook",
|
||||||
|
".tcl": "text/x-scripttcl",
|
||||||
|
".tcsh": "text/x-scripttcsh",
|
||||||
|
".tex": "application/x-tex",
|
||||||
|
".texi": "application/x-texinfo",
|
||||||
|
".texinfo": "application/x-texinfo",
|
||||||
|
".text": "text/plain",
|
||||||
|
".tgz": "application/gnutar",
|
||||||
|
".tif": "image/tiff",
|
||||||
|
".tiff": "image/tiff",
|
||||||
|
".tr": "application/x-troff",
|
||||||
|
".tsi": "audio/tsp-audio",
|
||||||
|
".tsp": "application/dsptype",
|
||||||
|
".tsv": "text/tab-separated-values",
|
||||||
|
".turbot": "image/florian",
|
||||||
|
".txt": "text/plain",
|
||||||
|
".uil": "text/x-uil",
|
||||||
|
".uni": "text/uri-list",
|
||||||
|
".unis": "text/uri-list",
|
||||||
|
".unv": "application/i-deas",
|
||||||
|
".uri": "text/uri-list",
|
||||||
|
".uris": "text/uri-list",
|
||||||
|
".ustar": "application/x-ustar",
|
||||||
|
".uu": "text/x-uuencode",
|
||||||
|
".uue": "text/x-uuencode",
|
||||||
|
".vcd": "application/x-cdlink",
|
||||||
|
".vcf": "text/x-vcard",
|
||||||
|
".vcard": "text/x-vcard",
|
||||||
|
".vcs": "text/x-vcalendar",
|
||||||
|
".vda": "application/vda",
|
||||||
|
".vdo": "video/vdo",
|
||||||
|
".vew": "application/groupwise",
|
||||||
|
".viv": "video/vivo",
|
||||||
|
".vivo": "video/vivo",
|
||||||
|
".vmd": "application/vocaltec-media-desc",
|
||||||
|
".vmf": "application/vocaltec-media-file",
|
||||||
|
".voc": "audio/voc",
|
||||||
|
".vos": "video/vosaic",
|
||||||
|
".vox": "audio/voxware",
|
||||||
|
".vqe": "audio/x-twinvq-plugin",
|
||||||
|
".vqf": "audio/x-twinvq",
|
||||||
|
".vql": "audio/x-twinvq-plugin",
|
||||||
|
".vrml": "application/x-vrml",
|
||||||
|
".vrt": "x-world/x-vrt",
|
||||||
|
".vsd": "application/x-visio",
|
||||||
|
".vst": "application/x-visio",
|
||||||
|
".vsw": "application/x-visio",
|
||||||
|
".w60": "application/wordperfect60",
|
||||||
|
".w61": "application/wordperfect61",
|
||||||
|
".w6w": "application/msword",
|
||||||
|
".wav": "audio/wav",
|
||||||
|
".wb1": "application/x-qpro",
|
||||||
|
".wbmp": "image/vnd.wap.wbmp",
|
||||||
|
".web": "application/vndxara",
|
||||||
|
".wiz": "application/msword",
|
||||||
|
".wk1": "application/x-123",
|
||||||
|
".wmf": "windows/metafile",
|
||||||
|
".wml": "text/vnd.wap.wml",
|
||||||
|
".wmlc": "application/vnd.wap.wmlc",
|
||||||
|
".wmls": "text/vnd.wap.wmlscript",
|
||||||
|
".wmlsc": "application/vnd.wap.wmlscriptc",
|
||||||
|
".word": "application/msword",
|
||||||
|
".wp5": "application/wordperfect",
|
||||||
|
".wp6": "application/wordperfect",
|
||||||
|
".wp": "application/wordperfect",
|
||||||
|
".wpd": "application/wordperfect",
|
||||||
|
".wq1": "application/x-lotus",
|
||||||
|
".wri": "application/mswrite",
|
||||||
|
".wrl": "application/x-world",
|
||||||
|
".wrz": "model/vrml",
|
||||||
|
".wsc": "text/scriplet",
|
||||||
|
".wsrc": "application/x-wais-source",
|
||||||
|
".wtk": "application/x-wintalk",
|
||||||
|
".x-png": "image/png",
|
||||||
|
".xbm": "image/x-xbitmap",
|
||||||
|
".xdr": "video/x-amt-demorun",
|
||||||
|
".xgz": "xgl/drawing",
|
||||||
|
".xif": "image/vndxiff",
|
||||||
|
".xl": "application/excel",
|
||||||
|
".xla": "application/excel",
|
||||||
|
".xlb": "application/excel",
|
||||||
|
".xlc": "application/excel",
|
||||||
|
".xld": "application/excel",
|
||||||
|
".xlk": "application/excel",
|
||||||
|
".xll": "application/excel",
|
||||||
|
".xlm": "application/excel",
|
||||||
|
".xls": "application/excel",
|
||||||
|
".xlt": "application/excel",
|
||||||
|
".xlv": "application/excel",
|
||||||
|
".xlw": "application/excel",
|
||||||
|
".xm": "audio/xm",
|
||||||
|
".xml": "text/xml",
|
||||||
|
".xmz": "xgl/movie",
|
||||||
|
".xpix": "application/x-vndls-xpix",
|
||||||
|
".xpm": "image/x-xpixmap",
|
||||||
|
".xsr": "video/x-amt-showrun",
|
||||||
|
".xwd": "image/x-xwd",
|
||||||
|
".xyz": "chemical/x-pdb",
|
||||||
|
".z": "application/x-compress",
|
||||||
|
".zip": "application/zip",
|
||||||
|
".zoo": "application/octet-stream",
|
||||||
|
".zsh": "text/x-scriptzsh",
|
||||||
|
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
".docm": "application/vnd.ms-word.document.macroEnabled.12",
|
||||||
|
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||||
|
".dotm": "application/vnd.ms-word.template.macroEnabled.12",
|
||||||
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||||
|
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||||
|
".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
|
||||||
|
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||||
|
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
|
||||||
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||||
|
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||||
|
".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
||||||
|
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||||
|
".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||||
|
".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
||||||
|
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
||||||
|
".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
|
||||||
|
".thmx": "application/vnd.ms-officetheme",
|
||||||
|
".onetoc": "application/onenote",
|
||||||
|
".onetoc2": "application/onenote",
|
||||||
|
".onetmp": "application/onenote",
|
||||||
|
".onepkg": "application/onenote",
|
||||||
|
".key": "application/x-iwork-keynote-sffkey",
|
||||||
|
".kth": "application/x-iwork-keynote-sffkth",
|
||||||
|
".nmbtemplate": "application/x-iwork-numbers-sfftemplate",
|
||||||
|
".numbers": "application/x-iwork-numbers-sffnumbers",
|
||||||
|
".pages": "application/x-iwork-pages-sffpages",
|
||||||
|
".template": "application/x-iwork-pages-sfftemplate",
|
||||||
|
".xpi": "application/x-xpinstall",
|
||||||
|
".oex": "application/x-opera-extension",
|
||||||
|
".mustache": "text/html",
|
||||||
|
}
|
||||||
56
vendor/code.hoteas.com/golang/hotime/session.go
generated
vendored
Normal file
56
vendor/code.hoteas.com/golang/hotime/session.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/cache"
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
//session对象
|
||||||
|
type SessionIns struct {
|
||||||
|
*HoTimeCache
|
||||||
|
SessionId string
|
||||||
|
Map
|
||||||
|
ContextBase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *SessionIns) set() {
|
||||||
|
that.HoTimeCache.Session(HEAD_SESSION_ADD+that.SessionId, that.Map)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *SessionIns) Session(key string, data ...interface{}) *Obj {
|
||||||
|
|
||||||
|
if that.Map == nil {
|
||||||
|
that.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) != 0 {
|
||||||
|
if data[0] == nil {
|
||||||
|
delete(that.Map, key)
|
||||||
|
that.set()
|
||||||
|
} else {
|
||||||
|
that.Map[key] = data[0]
|
||||||
|
that.set()
|
||||||
|
}
|
||||||
|
return &Obj{Data: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Obj{Data: that.Map.Get(key)}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *SessionIns) get() {
|
||||||
|
|
||||||
|
that.Map = that.HoTimeCache.Session(HEAD_SESSION_ADD + that.SessionId).ToMap()
|
||||||
|
if that.Map != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
that.Map = Map{}
|
||||||
|
that.HoTimeCache.Session(HEAD_SESSION_ADD+that.SessionId, that.Map)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (that *SessionIns) Init(cache *HoTimeCache) {
|
||||||
|
that.HoTimeCache = cache
|
||||||
|
}
|
||||||
8
vendor/code.hoteas.com/golang/hotime/type.go
generated
vendored
Normal file
8
vendor/code.hoteas.com/golang/hotime/type.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
// Ctr 控制器
|
||||||
|
type Ctr map[string]Method
|
||||||
|
type Proj map[string]Ctr
|
||||||
|
type Router map[string]Proj
|
||||||
|
type MethodRouter map[string]Method //直接字符串关联函数
|
||||||
|
type Method func(that *Context)
|
||||||
137
vendor/code.hoteas.com/golang/hotime/var.go
generated
vendored
Normal file
137
vendor/code.hoteas.com/golang/hotime/var.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package hotime
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "code.hoteas.com/golang/hotime/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var IsRun = false //当前状态
|
||||||
|
var App = map[string]*Application{} //整个项目
|
||||||
|
|
||||||
|
//var Db = HoTimeDB{} //数据库实例
|
||||||
|
|
||||||
|
var Config = Map{
|
||||||
|
"mode": 2, //模式 0生产模式,1,测试模式,2,开发模式
|
||||||
|
//"codeConfig": Map{
|
||||||
|
// "admin": "config/app.json",
|
||||||
|
//},
|
||||||
|
"codeConfig": Slice{
|
||||||
|
Map{
|
||||||
|
"table": "admin", //默认admin,必须,有则根据数据库内当前表名做为用户生成数据
|
||||||
|
"name": "", //默认admin,非必须,有则生成代码到此目录,无则采用缺省模式使用表名
|
||||||
|
"config": "config/app.json", //默认config/app.json,必须,接口描述配置文件,无则自动生成
|
||||||
|
"rule": "config/rule.json", //默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成
|
||||||
|
"mode": 0, //默认0,非必须,0为内嵌代码模式,1为生成代码模式
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"db": Map{
|
||||||
|
"sqlite": Map{
|
||||||
|
"path": "config/data.db",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cache": Map{
|
||||||
|
"memory": Map{
|
||||||
|
"timeout": 60 * 60 * 2,
|
||||||
|
"db": true,
|
||||||
|
"session": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"error": Map{
|
||||||
|
"1": "内部系统异常",
|
||||||
|
"2": "访问权限异常",
|
||||||
|
"3": "请求参数异常",
|
||||||
|
"4": "数据处理异常",
|
||||||
|
"5": "数据结果异常",
|
||||||
|
},
|
||||||
|
"tpt": "tpt",
|
||||||
|
"defFile": []string{"index.html", "index.htm"},
|
||||||
|
"port": "80",
|
||||||
|
"sessionName": "HOTIME",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ConfigNote = Map{
|
||||||
|
"logLevel": "默认0,必须,0关闭,1打印,日志等级",
|
||||||
|
"logFile": "无默认,非必须,如果需要存储日志文件时使用,保存格式为:a/b/c/20060102150405.txt,将生成:a/b/c/年月日时分秒.txt,按需设置",
|
||||||
|
"webConnectLogShow": "默认true,非必须,访问日志如果需要web访问链接、访问ip、访问时间打印,false为关闭true开启此功能",
|
||||||
|
"webConnectLogFile": "无默认,非必须,webConnectLogShow开启之后才能使用,如果需要存储日志文件时使用,保存格式为:a/b/c/20060102150405.txt,将生成:a/b/c/年月日时分秒.txt,按需设置",
|
||||||
|
"mode": "默认0,非必须,0生产模式,1,测试模式,2开发模式,3内嵌代码模式,在开发模式下会显示更多的数据用于开发测试,并能够辅助研发,自动生成配置文件、代码等功能,web无缓存,数据库不启用缓存", //debug 0关闭1开启
|
||||||
|
//"codeConfig": Map{
|
||||||
|
// "注释": "配置即启用,非必须,默认无",
|
||||||
|
// //"package":"默认admin,必须,mode模式为2时会自动生成包文件夹和代码文件",
|
||||||
|
// //"path":""
|
||||||
|
//
|
||||||
|
// "packageName": "默认无,必须,包名称以及应用名,生成代码的配置文件地址,比如config/app.json,数据库有更新时自动更新配置文件以及对应的生成文件",
|
||||||
|
//},
|
||||||
|
"codeConfig": Slice{
|
||||||
|
"注释:配置即启用,非必须,默认无",
|
||||||
|
Map{ //默认无,必须,接口类别名称
|
||||||
|
//"注释": "", //
|
||||||
|
"table": "默认admin,必须,根据数据库内当前表名做为用户生成数据",
|
||||||
|
"name": "默认无,非必须,有则生成代码到此目录,无则采用缺省模式使用表名,如设置为:admin,将在admin目录生成包名为admin的代码",
|
||||||
|
"config": "默认config/app.json,必须,接口描述配置文件", //
|
||||||
|
"configDB": "默认无,非必须,有则每次将数据库数据生成到此目录用于配置读写,无则不生成", //
|
||||||
|
"rule": "默认config/rule.json,非必须,有则按改规则生成接口,无则按系统内嵌方式生成",
|
||||||
|
"mode": "默认0,非必须,0为内嵌代码模式,1为生成代码模式",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"db": Map{
|
||||||
|
"注释": "配置即启用,非必须,默认使用sqlite数据库",
|
||||||
|
"mysql": Map{
|
||||||
|
"注释": "除prefix及主从数据库slave项,其他全部必须",
|
||||||
|
"host": "默认127.0.0.1,必须,数据库ip地址",
|
||||||
|
"name": "默认test,必须,数据库名称",
|
||||||
|
"user": "默认root,必须,数据库用户名",
|
||||||
|
"password": "默认root,必须,数据库密码",
|
||||||
|
"port": "默认3306,必须,数据库端口",
|
||||||
|
"prefix": "默认空,非必须,数据表前缀",
|
||||||
|
"slave": Map{
|
||||||
|
"注释": "从数据库配置,mysql里配置slave项即启用主从读写,减少数据库压力",
|
||||||
|
"host": "默认127.0.0.1,必须,数据库ip地址",
|
||||||
|
"name": "默认test,必须,数据库名称",
|
||||||
|
"user": "默认root,必须,数据库用户名",
|
||||||
|
"password": "默认root,必须,数据库密码",
|
||||||
|
"port": "默认3306,必须,数据库端口",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sqlite": Map{
|
||||||
|
"path": "默认config/data.db,必须,数据库位置",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cache": Map{
|
||||||
|
"注释": "可配置memory,db,redis,默认启用memory,默认优先级为memory>redis>db,memory与数据库缓存设置项一致,缓存数据填充会自动反方向反哺,加入memory缓存过期将自动从redis更新,但memory永远不会更新redis,如果是集群建议不要开启memory,配置即启用",
|
||||||
|
"memory": Map{
|
||||||
|
"timeout": "默认60 * 60 * 2,非必须,过期时间,超时自动删除",
|
||||||
|
"db": "默认true,非必须,缓存数据库,启用后能减少数据库的读写压力",
|
||||||
|
"session": "默认true,非必须,缓存web session,同时缓存session保持的用户缓存",
|
||||||
|
},
|
||||||
|
"db": Map{
|
||||||
|
"timeout": "默认60 * 60 * 24 * 30,非必须,过期时间,超时自动删除",
|
||||||
|
"db": "默认false,非必须,缓存数据库,启用后能减少数据库的读写压力",
|
||||||
|
"session": "默认true,非必须,缓存web session,同时缓存session保持的用户缓存",
|
||||||
|
},
|
||||||
|
"redis": Map{
|
||||||
|
"host": "默认服务ip:127.0.0.1,必须,如果需要使用redis服务时配置,",
|
||||||
|
"port": "默认服务端口:6379,必须,如果需要使用redis服务时配置,",
|
||||||
|
"password": "默认密码空,必须,如果需要使用redis服务时配置,默认密码空",
|
||||||
|
"timeout": "默认60 * 60 * 24 * 15,非必须,过期时间,超时自动删除",
|
||||||
|
"db": "默认true,非必须,缓存数据库,启用后能减少数据库的读写压力",
|
||||||
|
"session": "默认true,非必须,缓存web session,同时缓存session保持的用户缓存",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"error": Map{
|
||||||
|
"1": "内部系统异常,在环境配置,文件访问权限等基础运行环境条件不足造成严重错误时使用",
|
||||||
|
"2": "访问权限异常,没有登录或者登录异常等时候使用",
|
||||||
|
"3": "请求参数异常,request参数不满足要求,比如参数不足,参数类型错误,参数不满足要求等时候使用",
|
||||||
|
"4": "数据处理异常,数据库操作或者三方请求返回的结果非正常结果,比如数据库突然中断等时候使用",
|
||||||
|
"5": "数据结果异常,一般用于无法给出response要求的格式要求下使用,比如response需要的是string格式但你只能提供int数据时",
|
||||||
|
"注释": "web服务内置错误提示,自定义异常建议10开始",
|
||||||
|
},
|
||||||
|
"tpt": "默认tpt,必须,web静态文件目录,默认为程序目录下tpt目录",
|
||||||
|
"defFile": "默认访问index.html或者index.htm文件,必须,默认访问文件类型",
|
||||||
|
"crossDomain": "默认空 非必须,空字符串为不开启,如果需要跨域设置,auto为智能开启所有网站允许跨域,http://www.baidu.com为指定域允许跨域", //是否开启跨域
|
||||||
|
"modeRouterStrict": "默认false,必须,路由严格模式false,为大小写忽略必须匹配,true必须大小写匹配", //路由严格模式/a/b/c
|
||||||
|
"sessionName": "默认HOTIME,必须,设置session的cookie名",
|
||||||
|
"port": "默认80,必须,web服务开启Http端口,0为不启用http服务,默认80",
|
||||||
|
"tlsPort": "默认空,非必须,web服务https端口,0为不启用https服务",
|
||||||
|
"tlsKey": "默认空,非必须,https密钥",
|
||||||
|
"tlsCert": "默认空,非必须,https证书",
|
||||||
|
}
|
||||||
1
vendor/github.com/360EntSecGroup-Skylar/excelize/.gitignore
generated
vendored
Normal file
1
vendor/github.com/360EntSecGroup-Skylar/excelize/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
test/Test*.xlsx
|
||||||
26
vendor/github.com/360EntSecGroup-Skylar/excelize/.travis.yml
generated
vendored
Normal file
26
vendor/github.com/360EntSecGroup-Skylar/excelize/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -d -t -v ./... && go build -v ./...
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
|
env:
|
||||||
|
matrix:
|
||||||
|
- GOARCH=amd64
|
||||||
|
- GOARCH=386
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go vet ./...
|
||||||
|
- go test ./... -v -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
46
vendor/github.com/360EntSecGroup-Skylar/excelize/CODE_OF_CONDUCT.md
generated
vendored
Normal file
46
vendor/github.com/360EntSecGroup-Skylar/excelize/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [xuri.me](https://xuri.me). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
464
vendor/github.com/360EntSecGroup-Skylar/excelize/CONTRIBUTING.md
generated
vendored
Normal file
464
vendor/github.com/360EntSecGroup-Skylar/excelize/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
<!-- use this template to generate the contributor docs with the following command: `$ lingo run docs --template CONTRIBUTING_TEMPLATE.md --output CONTRIBUTING.md` -->
|
||||||
|
# Contributing to excelize
|
||||||
|
|
||||||
|
Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
|
||||||
|
guidelines useful to experienced open source contributors. Finally, make sure
|
||||||
|
you read our [community guidelines](#community-guidelines) before you
|
||||||
|
start participating.
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
* [Reporting Security Issues](#reporting-security-issues)
|
||||||
|
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
||||||
|
* [Reporting Issues](#reporting-other-issues)
|
||||||
|
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
||||||
|
* [Community Guidelines](#community-guidelines)
|
||||||
|
|
||||||
|
## Reporting security issues
|
||||||
|
|
||||||
|
The excelize maintainers take security seriously. If you discover a security
|
||||||
|
issue, please bring it to their attention right away!
|
||||||
|
|
||||||
|
Please **DO NOT** file a public issue, instead send your report privately to
|
||||||
|
[xuri.me](https://xuri.me).
|
||||||
|
|
||||||
|
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||||
|
We currently do not offer a paid security bounty program, but are not
|
||||||
|
ruling it out in the future.
|
||||||
|
|
||||||
|
## Reporting other issues
|
||||||
|
|
||||||
|
A great way to contribute to the project is to send a detailed report when you
|
||||||
|
encounter an issue. We always appreciate a well-written, thorough bug report,
|
||||||
|
and will thank you for it!
|
||||||
|
|
||||||
|
Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
|
||||||
|
doesn't already include that problem or suggestion before submitting an issue.
|
||||||
|
If you find a match, you can use the "subscribe" button to get notified on
|
||||||
|
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
||||||
|
only clutter the discussion, and don't help resolving it. However, if you
|
||||||
|
have ways to reproduce the issue or have additional information that may help
|
||||||
|
resolving the issue, please leave a comment.
|
||||||
|
|
||||||
|
When reporting issues, always include the output of `go env`.
|
||||||
|
|
||||||
|
Also include the steps required to reproduce the problem if possible and
|
||||||
|
applicable. This information will help us review and fix your issue faster.
|
||||||
|
When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
|
||||||
|
Don't forget to remove sensitive data from your logfiles before posting (you can
|
||||||
|
replace those parts with "REDACTED").
|
||||||
|
|
||||||
|
## Quick contribution tips and guidelines
|
||||||
|
|
||||||
|
This section gives the experienced contributor some tips and guidelines.
|
||||||
|
|
||||||
|
### Pull requests are always welcome
|
||||||
|
|
||||||
|
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||||
|
it? Do it! We will appreciate it. Any significant improvement should be
|
||||||
|
documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
|
||||||
|
anybody starts working on it.
|
||||||
|
|
||||||
|
We are always thrilled to receive pull requests. We do our best to process them
|
||||||
|
quickly. If your pull request is not accepted on the first try,
|
||||||
|
don't get discouraged!
|
||||||
|
|
||||||
|
### Design and cleanup proposals
|
||||||
|
|
||||||
|
You can propose new designs for existing excelize features. You can also design
|
||||||
|
entirely new features. We really appreciate contributors who want to refactor or
|
||||||
|
otherwise cleanup our project.
|
||||||
|
|
||||||
|
We try hard to keep excelize lean and focused. Excelize can't do everything for
|
||||||
|
everybody. This means that we might decide against incorporating a new feature.
|
||||||
|
However, there might be a way to implement that feature *on top of* excelize.
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
|
||||||
|
Fork the repository and make changes on your fork in a feature branch:
|
||||||
|
|
||||||
|
* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
|
||||||
|
the issue.
|
||||||
|
* If it's a feature branch, create an enhancement issue to announce
|
||||||
|
your intentions, and name it XXXX-something where XXXX is the number of the
|
||||||
|
issue.
|
||||||
|
|
||||||
|
Submit unit tests for your changes. Go has a great test framework built in; use
|
||||||
|
it! Take a look at existing tests for inspiration. Run the full test on your branch before
|
||||||
|
submitting a pull request.
|
||||||
|
|
||||||
|
Update the documentation when creating or modifying features. Test your
|
||||||
|
documentation changes for clarity, concision, and correctness, as well as a
|
||||||
|
clean documentation build.
|
||||||
|
|
||||||
|
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||||
|
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||||
|
committing your changes. Most editors have plug-ins that do this automatically.
|
||||||
|
|
||||||
|
Pull request descriptions should be as clear as possible and include a reference
|
||||||
|
to all the issues that they address.
|
||||||
|
|
||||||
|
### Successful Changes
|
||||||
|
|
||||||
|
Before contributing large or high impact changes, make the effort to coordinate
|
||||||
|
with the maintainers of the project before submitting a pull request. This
|
||||||
|
prevents you from doing extra work that may or may not be merged.
|
||||||
|
|
||||||
|
Large PRs that are just submitted without any prior communication are unlikely
|
||||||
|
to be successful.
|
||||||
|
|
||||||
|
While pull requests are the methodology for submitting changes to code, changes
|
||||||
|
are much more likely to be accepted if they are accompanied by additional
|
||||||
|
engineering work. While we don't define this explicitly, most of these goals
|
||||||
|
are accomplished through communication of the design goals and subsequent
|
||||||
|
solutions. Often times, it helps to first state the problem before presenting
|
||||||
|
solutions.
|
||||||
|
|
||||||
|
Typically, the best methods of accomplishing this are to submit an issue,
|
||||||
|
stating the problem. This issue can include a problem statement and a
|
||||||
|
checklist with requirements. If solutions are proposed, alternatives should be
|
||||||
|
listed and eliminated. Even if the criteria for elimination of a solution is
|
||||||
|
frivolous, say so.
|
||||||
|
|
||||||
|
Larger changes typically work best with design documents. These are focused on
|
||||||
|
providing context to the design at the time the feature was conceived and can
|
||||||
|
inform future documentation contributions.
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
Commit messages must start with a capitalized and short summary
|
||||||
|
written in the imperative, followed by an optional, more detailed explanatory
|
||||||
|
text which is separated from the summary by an empty line.
|
||||||
|
|
||||||
|
Commit messages should follow best practices, including explaining the context
|
||||||
|
of the problem and how it was solved, including in caveats or follow up changes
|
||||||
|
required. They should tell the story of the change and provide readers
|
||||||
|
understanding of what led to it.
|
||||||
|
|
||||||
|
In practice, the best approach to maintaining a nice commit message is to
|
||||||
|
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
||||||
|
changeset. This allows one to piece together a change, as information becomes
|
||||||
|
available.
|
||||||
|
|
||||||
|
If you squash a series of commits, don't just submit that. Re-write the commit
|
||||||
|
message, as if the series of commits was a single stroke of brilliance.
|
||||||
|
|
||||||
|
That said, there is no requirement to have a single commit for a PR, as long as
|
||||||
|
each commit tells the story. For example, if there is a feature that requires a
|
||||||
|
package, it might make sense to have the package in a separate commit then have
|
||||||
|
a subsequent commit that uses it.
|
||||||
|
|
||||||
|
Remember, you're telling part of the story with the commit message. Don't make
|
||||||
|
your chapter weird.
|
||||||
|
|
||||||
|
### Review
|
||||||
|
|
||||||
|
Code review comments may be added to your pull request. Discuss, then make the
|
||||||
|
suggested modifications and push additional commits to your feature branch. Post
|
||||||
|
a comment after pushing. New commits show up in the pull request automatically,
|
||||||
|
but the reviewers are notified only when you comment.
|
||||||
|
|
||||||
|
Pull requests must be cleanly rebased on top of master without multiple branches
|
||||||
|
mixed into the PR.
|
||||||
|
|
||||||
|
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
|
||||||
|
feature branch to update your pull request rather than `merge master`.
|
||||||
|
|
||||||
|
Before you make a pull request, squash your commits into logical units of work
|
||||||
|
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
|
||||||
|
set of patches that should be reviewed together: for example, upgrading the
|
||||||
|
version of a vendored dependency and taking advantage of its now available new
|
||||||
|
feature constitute two separate units of work. Implementing a new function and
|
||||||
|
calling it in another file constitute a single logical unit of work. The very
|
||||||
|
high majority of submissions should have a single commit, so if in doubt: squash
|
||||||
|
down to one.
|
||||||
|
|
||||||
|
After every commit, make sure the test passes. Include documentation
|
||||||
|
changes in the same pull request so that a revert would remove all traces of
|
||||||
|
the feature or fix.
|
||||||
|
|
||||||
|
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
||||||
|
close an issue. Including references automatically closes the issue on a merge.
|
||||||
|
|
||||||
|
Please see the [Coding Style](#coding-style) for further guidelines.
|
||||||
|
|
||||||
|
### Merge approval
|
||||||
|
|
||||||
|
The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||||
|
indicate acceptance.
|
||||||
|
|
||||||
|
### Sign your work
|
||||||
|
|
||||||
|
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||||
|
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||||
|
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||||
|
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
1 Letterman Drive
|
||||||
|
Suite D4700
|
||||||
|
San Francisco, CA, 94129
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you just add a line to every git commit message:
|
||||||
|
|
||||||
|
Signed-off-by: Ri Xu https://xuri.me
|
||||||
|
|
||||||
|
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
|
|
||||||
|
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||||
|
commit automatically with `git commit -s`.
|
||||||
|
|
||||||
|
### How can I become a maintainer
|
||||||
|
|
||||||
|
First, all maintainers have 3 things
|
||||||
|
|
||||||
|
* They share responsibility in the project's success.
|
||||||
|
* They have made a long-term, recurring time investment to improve the project.
|
||||||
|
* They spend that time doing whatever needs to be done, not necessarily what
|
||||||
|
is the most interesting or fun.
|
||||||
|
|
||||||
|
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||||
|
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||||
|
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||||
|
or the reliability of a release process. But those things distinguish a good
|
||||||
|
project from a great one.
|
||||||
|
|
||||||
|
Don't forget: being a maintainer is a time investment. Make sure you
|
||||||
|
will have time to make yourself available. You don't have to be a
|
||||||
|
maintainer to make a difference on the project!
|
||||||
|
|
||||||
|
If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
|
||||||
|
|
||||||
|
## Community guidelines
|
||||||
|
|
||||||
|
We want to keep the community awesome, growing and collaborative. We need
|
||||||
|
your help to keep it that way. To help with this we've come up with some general
|
||||||
|
guidelines for the community as a whole:
|
||||||
|
|
||||||
|
* Be nice: Be courteous, respectful and polite to fellow community members:
|
||||||
|
no regional, racial, gender, or other abuse will be tolerated. We like
|
||||||
|
nice people way better than mean ones!
|
||||||
|
|
||||||
|
* Encourage diversity and participation: Make everyone in our community feel
|
||||||
|
welcome, regardless of their background and the extent of their
|
||||||
|
contributions, and do everything possible to encourage participation in
|
||||||
|
our community.
|
||||||
|
|
||||||
|
* Keep it legal: Basically, don't get us in trouble. Share only content that
|
||||||
|
you own, do not share private or sensitive information, and don't break
|
||||||
|
the law.
|
||||||
|
|
||||||
|
* Stay on topic: Make sure that you are posting to the correct channel and
|
||||||
|
avoid off-topic discussions. Remember when you update an issue or respond
|
||||||
|
to an email you are potentially sending to a large number of people. Please
|
||||||
|
consider this before you update. Also remember that nobody likes spam.
|
||||||
|
|
||||||
|
* Don't send email to the maintainers: There's no need to send email to the
|
||||||
|
maintainers to ask them to investigate an issue or to take a look at a
|
||||||
|
pull request. Instead of sending an email, GitHub mentions should be
|
||||||
|
used to ping maintainers to review a pull request, a proposal or an
|
||||||
|
issue.
|
||||||
|
|
||||||
|
### Guideline violations — 3 strikes method
|
||||||
|
|
||||||
|
The point of this section is not to find opportunities to punish people, but we
|
||||||
|
do need a fair way to deal with people who are making our community suck.
|
||||||
|
|
||||||
|
1. First occurrence: We'll give you a friendly, but public reminder that the
|
||||||
|
behavior is inappropriate according to our guidelines.
|
||||||
|
|
||||||
|
2. Second occurrence: We will send you a private message with a warning that
|
||||||
|
any additional violations will result in removal from the community.
|
||||||
|
|
||||||
|
3. Third occurrence: Depending on the violation, we may need to delete or ban
|
||||||
|
your account.
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
|
||||||
|
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
|
||||||
|
have spam all over the place.
|
||||||
|
|
||||||
|
* Violations are forgiven after 6 months of good behavior, and we won't hold a
|
||||||
|
grudge.
|
||||||
|
|
||||||
|
* People who commit minor infractions will get some education, rather than
|
||||||
|
hammering them in the 3 strikes process.
|
||||||
|
|
||||||
|
* The rules apply equally to everyone in the community, no matter how much
|
||||||
|
you've contributed.
|
||||||
|
|
||||||
|
* Extreme violations of a threatening, abusive, destructive or illegal nature
|
||||||
|
will be addressed immediately and are not subject to 3 strikes or forgiveness.
|
||||||
|
|
||||||
|
* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
|
||||||
|
appeals, we know that mistakes happen, and we'll work with you to come up with a
|
||||||
|
fair solution if there has been a misunderstanding.
|
||||||
|
|
||||||
|
## Coding Style
|
||||||
|
|
||||||
|
Unless explicitly stated, we follow all coding guidelines from the Go
|
||||||
|
community. While some of these standards may seem arbitrary, they somehow seem
|
||||||
|
to result in a solid, consistent codebase.
|
||||||
|
|
||||||
|
It is possible that the code base does not currently comply with these
|
||||||
|
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||||
|
goes against the spirit of the guidelines. All new contributions should make a
|
||||||
|
best effort to clean up and make the code base better than they left it.
|
||||||
|
Obviously, apply your best judgement. Remember, the goal here is to make the
|
||||||
|
code base easier for humans to navigate and understand. Always keep that in
|
||||||
|
mind when nudging others to comply.
|
||||||
|
|
||||||
|
The rules:
|
||||||
|
|
||||||
|
1. All code should be formatted with `gofmt -s`.
|
||||||
|
2. All code should pass the default levels of
|
||||||
|
[`golint`](https://github.com/golang/lint).
|
||||||
|
3. All code should follow the guidelines covered in [Effective
|
||||||
|
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||||
|
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
4. Comment the code. Tell us the why, the history and the context.
|
||||||
|
5. Document _all_ declarations and methods, even private ones. Declare
|
||||||
|
expectations, caveats and anything else that may be important. If a type
|
||||||
|
gets exported, having the comments already there will ensure it's ready.
|
||||||
|
6. Variable name length should be proportional to its context and no longer.
|
||||||
|
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||||
|
In practice, short methods will have short variable names and globals will
|
||||||
|
have longer names.
|
||||||
|
7. No underscores in package names. If you need a compound name, step back,
|
||||||
|
and re-examine why you need a compound name. If you still think you need a
|
||||||
|
compound name, lose the underscore.
|
||||||
|
8. No utils or helpers packages. If a function is not general enough to
|
||||||
|
warrant its own package, it has not been written generally enough to be a
|
||||||
|
part of a util package. Just leave it unexported and well-documented.
|
||||||
|
9. All tests should run with `go test` and outside tooling should not be
|
||||||
|
required. No, we don't need another unit testing framework. Assertion
|
||||||
|
packages are acceptable if they provide _real_ incremental value.
|
||||||
|
10. Even though we call these "rules" above, they are actually just
|
||||||
|
guidelines. Since you've read all the rules, you now know that.
|
||||||
|
|
||||||
|
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||||
|
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||||
|
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||||
|
kool-aid is a lot easier than going thirsty.
|
||||||
|
|
||||||
|
## Code Review Comments and Effective Go Guidelines
|
||||||
|
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
|
||||||
|
|
||||||
|
### Package Comment
|
||||||
|
Every package should have a package comment, a block comment preceding the package clause.
|
||||||
|
For multi-file packages, the package comment only needs to be present in one file, and any one will do.
|
||||||
|
The package comment should introduce the package and provide information relevant to the package as a
|
||||||
|
whole. It will appear first on the godoc page and should set up the detailed documentation that follows.
|
||||||
|
|
||||||
|
|
||||||
|
### Single Method Interface Name
|
||||||
|
By convention, one-method interfaces are named by the method name plus an -er suffix
|
||||||
|
or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
|
||||||
|
|
||||||
|
There are a number of such names and it's productive to honor them and the function names they capture.
|
||||||
|
Read, Write, Close, Flush, String and so on have canonical signatures and meanings. To avoid confusion,
|
||||||
|
don't give your method one of those names unless it has the same signature and meaning. Conversely,
|
||||||
|
if your type implements a method with the same meaning as a method on a well-known type, give it the
|
||||||
|
same name and signature; call your string-converter method String not ToString.
|
||||||
|
|
||||||
|
|
||||||
|
### Avoid Annotations in Comments
|
||||||
|
Comments do not need extra formatting such as banners of stars. The generated output
|
||||||
|
may not even be presented in a fixed-width font, so don't depend on spacing for alignment—godoc,
|
||||||
|
like gofmt, takes care of that. The comments are uninterpreted plain text, so HTML and other
|
||||||
|
annotations such as _this_ will reproduce verbatim and should not be used. One adjustment godoc
|
||||||
|
does do is to display indented text in a fixed-width font, suitable for program snippets.
|
||||||
|
The package comment for the fmt package uses this to good effect.
|
||||||
|
|
||||||
|
|
||||||
|
### Comment First Word as Subject
|
||||||
|
Doc comments work best as complete sentences, which allow a wide variety of automated presentations.
|
||||||
|
The first sentence should be a one-sentence summary that starts with the name being declared.
|
||||||
|
|
||||||
|
|
||||||
|
### Good Package Name
|
||||||
|
It's helpful if everyone using the package can use the same name
|
||||||
|
to refer to its contents, which implies that the package name should
|
||||||
|
be good: short, concise, evocative. By convention, packages are
|
||||||
|
given lower case, single-word names; there should be no need for
|
||||||
|
underscores or mixedCaps. Err on the side of brevity, since everyone
|
||||||
|
using your package will be typing that name. And don't worry about
|
||||||
|
collisions a priori. The package name is only the default name for
|
||||||
|
imports; it need not be unique across all source code, and in the
|
||||||
|
rare case of a collision the importing package can choose a different
|
||||||
|
name to use locally. In any case, confusion is rare because the file
|
||||||
|
name in the import determines just which package is being used.
|
||||||
|
|
||||||
|
|
||||||
|
### Avoid Renaming Imports
|
||||||
|
Avoid renaming imports except to avoid a name collision; good package names
|
||||||
|
should not require renaming. In the event of collision, prefer to rename the
|
||||||
|
most local or project-specific import.
|
||||||
|
|
||||||
|
|
||||||
|
### Context as First Argument
|
||||||
|
Values of the context.Context type carry security credentials, tracing information,
|
||||||
|
deadlines, and cancellation signals across API and process boundaries. Go programs
|
||||||
|
pass Contexts explicitly along the entire function call chain from incoming RPCs
|
||||||
|
and HTTP requests to outgoing requests.
|
||||||
|
|
||||||
|
Most functions that use a Context should accept it as their first parameter.
|
||||||
|
|
||||||
|
|
||||||
|
### Do Not Discard Errors
|
||||||
|
Do not discard errors using _ variables. If a function returns an error,
|
||||||
|
check it to make sure the function succeeded. Handle the error, return it, or,
|
||||||
|
in truly exceptional situations, panic.
|
||||||
|
|
||||||
|
|
||||||
|
### Go Error Format
|
||||||
|
Error strings should not be capitalized (unless beginning with proper nouns
|
||||||
|
or acronyms) or end with punctuation, since they are usually printed following
|
||||||
|
other context. That is, use fmt.Errorf("something bad") not fmt.Errorf("Something bad"),
|
||||||
|
so that log.Printf("Reading %s: %v", filename, err) formats without a spurious
|
||||||
|
capital letter mid-message. This does not apply to logging, which is implicitly
|
||||||
|
line-oriented and not combined inside other messages.
|
||||||
|
|
||||||
|
|
||||||
|
### Use Crypto Rand
|
||||||
|
Do not use package math/rand to generate keys, even
|
||||||
|
throwaway ones. Unseeded, the generator is completely predictable.
|
||||||
|
Seeded with time.Nanoseconds(), there are just a few bits of entropy.
|
||||||
|
Instead, use crypto/rand's Reader, and if you need text, print to
|
||||||
|
hexadecimal or base64
|
||||||
|
|
||||||
384
vendor/github.com/360EntSecGroup-Skylar/excelize/CONTRIBUTING_TEMPLATE.md
generated
vendored
Normal file
384
vendor/github.com/360EntSecGroup-Skylar/excelize/CONTRIBUTING_TEMPLATE.md
generated
vendored
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
<!-- use this template to generate the contributor docs with the following command: `$ lingo run docs --template CONTRIBUTING_TEMPLATE.md --output CONTRIBUTING.md` -->
|
||||||
|
# Contributing to excelize
|
||||||
|
|
||||||
|
Want to hack on excelize? Awesome! This page contains information about reporting issues as well as some tips and
|
||||||
|
guidelines useful to experienced open source contributors. Finally, make sure
|
||||||
|
you read our [community guidelines](#community-guidelines) before you
|
||||||
|
start participating.
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
* [Reporting Security Issues](#reporting-security-issues)
|
||||||
|
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
|
||||||
|
* [Reporting Issues](#reporting-other-issues)
|
||||||
|
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
|
||||||
|
* [Community Guidelines](#community-guidelines)
|
||||||
|
|
||||||
|
## Reporting security issues
|
||||||
|
|
||||||
|
The excelize maintainers take security seriously. If you discover a security
|
||||||
|
issue, please bring it to their attention right away!
|
||||||
|
|
||||||
|
Please **DO NOT** file a public issue, instead send your report privately to
|
||||||
|
[xuri.me](https://xuri.me).
|
||||||
|
|
||||||
|
Security reports are greatly appreciated and we will publicly thank you for it.
|
||||||
|
We currently do not offer a paid security bounty program, but are not
|
||||||
|
ruling it out in the future.
|
||||||
|
|
||||||
|
## Reporting other issues
|
||||||
|
|
||||||
|
A great way to contribute to the project is to send a detailed report when you
|
||||||
|
encounter an issue. We always appreciate a well-written, thorough bug report,
|
||||||
|
and will thank you for it!
|
||||||
|
|
||||||
|
Check that [our issue database](https://github.com/360EntSecGroup-Skylar/excelize/issues)
|
||||||
|
doesn't already include that problem or suggestion before submitting an issue.
|
||||||
|
If you find a match, you can use the "subscribe" button to get notified on
|
||||||
|
updates. Do *not* leave random "+1" or "I have this too" comments, as they
|
||||||
|
only clutter the discussion, and don't help resolving it. However, if you
|
||||||
|
have ways to reproduce the issue or have additional information that may help
|
||||||
|
resolving the issue, please leave a comment.
|
||||||
|
|
||||||
|
When reporting issues, always include the output of `go env`.
|
||||||
|
|
||||||
|
Also include the steps required to reproduce the problem if possible and
|
||||||
|
applicable. This information will help us review and fix your issue faster.
|
||||||
|
When sending lengthy log-files, consider posting them as a gist [https://gist.github.com](https://gist.github.com).
|
||||||
|
Don't forget to remove sensitive data from your logfiles before posting (you can
|
||||||
|
replace those parts with "REDACTED").
|
||||||
|
|
||||||
|
## Quick contribution tips and guidelines
|
||||||
|
|
||||||
|
This section gives the experienced contributor some tips and guidelines.
|
||||||
|
|
||||||
|
### Pull requests are always welcome
|
||||||
|
|
||||||
|
Not sure if that typo is worth a pull request? Found a bug and know how to fix
|
||||||
|
it? Do it! We will appreciate it. Any significant improvement should be
|
||||||
|
documented as [a GitHub issue](https://github.com/360EntSecGroup-Skylar/excelize/issues) before
|
||||||
|
anybody starts working on it.
|
||||||
|
|
||||||
|
We are always thrilled to receive pull requests. We do our best to process them
|
||||||
|
quickly. If your pull request is not accepted on the first try,
|
||||||
|
don't get discouraged!
|
||||||
|
|
||||||
|
### Design and cleanup proposals
|
||||||
|
|
||||||
|
You can propose new designs for existing excelize features. You can also design
|
||||||
|
entirely new features. We really appreciate contributors who want to refactor or
|
||||||
|
otherwise cleanup our project.
|
||||||
|
|
||||||
|
We try hard to keep excelize lean and focused. Excelize can't do everything for
|
||||||
|
everybody. This means that we might decide against incorporating a new feature.
|
||||||
|
However, there might be a way to implement that feature *on top of* excelize.
|
||||||
|
|
||||||
|
### Conventions
|
||||||
|
|
||||||
|
Fork the repository and make changes on your fork in a feature branch:
|
||||||
|
|
||||||
|
* If it's a bug fix branch, name it XXXX-something where XXXX is the number of
|
||||||
|
the issue.
|
||||||
|
* If it's a feature branch, create an enhancement issue to announce
|
||||||
|
your intentions, and name it XXXX-something where XXXX is the number of the
|
||||||
|
issue.
|
||||||
|
|
||||||
|
Submit unit tests for your changes. Go has a great test framework built in; use
|
||||||
|
it! Take a look at existing tests for inspiration. Run the full test on your branch before
|
||||||
|
submitting a pull request.
|
||||||
|
|
||||||
|
Update the documentation when creating or modifying features. Test your
|
||||||
|
documentation changes for clarity, concision, and correctness, as well as a
|
||||||
|
clean documentation build.
|
||||||
|
|
||||||
|
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||||
|
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||||
|
committing your changes. Most editors have plug-ins that do this automatically.
|
||||||
|
|
||||||
|
Pull request descriptions should be as clear as possible and include a reference
|
||||||
|
to all the issues that they address.
|
||||||
|
|
||||||
|
### Successful Changes
|
||||||
|
|
||||||
|
Before contributing large or high impact changes, make the effort to coordinate
|
||||||
|
with the maintainers of the project before submitting a pull request. This
|
||||||
|
prevents you from doing extra work that may or may not be merged.
|
||||||
|
|
||||||
|
Large PRs that are just submitted without any prior communication are unlikely
|
||||||
|
to be successful.
|
||||||
|
|
||||||
|
While pull requests are the methodology for submitting changes to code, changes
|
||||||
|
are much more likely to be accepted if they are accompanied by additional
|
||||||
|
engineering work. While we don't define this explicitly, most of these goals
|
||||||
|
are accomplished through communication of the design goals and subsequent
|
||||||
|
solutions. Often times, it helps to first state the problem before presenting
|
||||||
|
solutions.
|
||||||
|
|
||||||
|
Typically, the best methods of accomplishing this are to submit an issue,
|
||||||
|
stating the problem. This issue can include a problem statement and a
|
||||||
|
checklist with requirements. If solutions are proposed, alternatives should be
|
||||||
|
listed and eliminated. Even if the criteria for elimination of a solution is
|
||||||
|
frivolous, say so.
|
||||||
|
|
||||||
|
Larger changes typically work best with design documents. These are focused on
|
||||||
|
providing context to the design at the time the feature was conceived and can
|
||||||
|
inform future documentation contributions.
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
Commit messages must start with a capitalized and short summary
|
||||||
|
written in the imperative, followed by an optional, more detailed explanatory
|
||||||
|
text which is separated from the summary by an empty line.
|
||||||
|
|
||||||
|
Commit messages should follow best practices, including explaining the context
|
||||||
|
of the problem and how it was solved, including in caveats or follow up changes
|
||||||
|
required. They should tell the story of the change and provide readers
|
||||||
|
understanding of what led to it.
|
||||||
|
|
||||||
|
In practice, the best approach to maintaining a nice commit message is to
|
||||||
|
leverage a `git add -p` and `git commit --amend` to formulate a solid
|
||||||
|
changeset. This allows one to piece together a change, as information becomes
|
||||||
|
available.
|
||||||
|
|
||||||
|
If you squash a series of commits, don't just submit that. Re-write the commit
|
||||||
|
message, as if the series of commits was a single stroke of brilliance.
|
||||||
|
|
||||||
|
That said, there is no requirement to have a single commit for a PR, as long as
|
||||||
|
each commit tells the story. For example, if there is a feature that requires a
|
||||||
|
package, it might make sense to have the package in a separate commit then have
|
||||||
|
a subsequent commit that uses it.
|
||||||
|
|
||||||
|
Remember, you're telling part of the story with the commit message. Don't make
|
||||||
|
your chapter weird.
|
||||||
|
|
||||||
|
### Review
|
||||||
|
|
||||||
|
Code review comments may be added to your pull request. Discuss, then make the
|
||||||
|
suggested modifications and push additional commits to your feature branch. Post
|
||||||
|
a comment after pushing. New commits show up in the pull request automatically,
|
||||||
|
but the reviewers are notified only when you comment.
|
||||||
|
|
||||||
|
Pull requests must be cleanly rebased on top of master without multiple branches
|
||||||
|
mixed into the PR.
|
||||||
|
|
||||||
|
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
|
||||||
|
feature branch to update your pull request rather than `merge master`.
|
||||||
|
|
||||||
|
Before you make a pull request, squash your commits into logical units of work
|
||||||
|
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
|
||||||
|
set of patches that should be reviewed together: for example, upgrading the
|
||||||
|
version of a vendored dependency and taking advantage of its now available new
|
||||||
|
feature constitute two separate units of work. Implementing a new function and
|
||||||
|
calling it in another file constitute a single logical unit of work. The very
|
||||||
|
high majority of submissions should have a single commit, so if in doubt: squash
|
||||||
|
down to one.
|
||||||
|
|
||||||
|
After every commit, make sure the test passes. Include documentation
|
||||||
|
changes in the same pull request so that a revert would remove all traces of
|
||||||
|
the feature or fix.
|
||||||
|
|
||||||
|
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in commits that
|
||||||
|
close an issue. Including references automatically closes the issue on a merge.
|
||||||
|
|
||||||
|
Please see the [Coding Style](#coding-style) for further guidelines.
|
||||||
|
|
||||||
|
### Merge approval
|
||||||
|
|
||||||
|
The excelize maintainers use LGTM (Looks Good To Me) in comments on the code review to
|
||||||
|
indicate acceptance.
|
||||||
|
|
||||||
|
### Sign your work
|
||||||
|
|
||||||
|
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||||
|
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||||
|
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||||
|
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||||
|
|
||||||
|
```text
|
||||||
|
Developer Certificate of Origin
|
||||||
|
Version 1.1
|
||||||
|
|
||||||
|
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||||
|
1 Letterman Drive
|
||||||
|
Suite D4700
|
||||||
|
San Francisco, CA, 94129
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this
|
||||||
|
license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you just add a line to every git commit message:
|
||||||
|
|
||||||
|
Signed-off-by: Ri Xu https://xuri.me
|
||||||
|
|
||||||
|
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||||
|
|
||||||
|
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||||
|
commit automatically with `git commit -s`.
|
||||||
|
|
||||||
|
### How can I become a maintainer
|
||||||
|
|
||||||
|
First, all maintainers have 3 things
|
||||||
|
|
||||||
|
* They share responsibility in the project's success.
|
||||||
|
* They have made a long-term, recurring time investment to improve the project.
|
||||||
|
* They spend that time doing whatever needs to be done, not necessarily what
|
||||||
|
is the most interesting or fun.
|
||||||
|
|
||||||
|
Maintainers are often under-appreciated, because their work is harder to appreciate.
|
||||||
|
It's easy to appreciate a really cool and technically advanced feature. It's harder
|
||||||
|
to appreciate the absence of bugs, the slow but steady improvement in stability,
|
||||||
|
or the reliability of a release process. But those things distinguish a good
|
||||||
|
project from a great one.
|
||||||
|
|
||||||
|
Don't forget: being a maintainer is a time investment. Make sure you
|
||||||
|
will have time to make yourself available. You don't have to be a
|
||||||
|
maintainer to make a difference on the project!
|
||||||
|
|
||||||
|
If you want to become a meintainer, contact [xuri.me](https://xuri.me) and given a introduction of you.
|
||||||
|
|
||||||
|
## Community guidelines
|
||||||
|
|
||||||
|
We want to keep the community awesome, growing and collaborative. We need
|
||||||
|
your help to keep it that way. To help with this we've come up with some general
|
||||||
|
guidelines for the community as a whole:
|
||||||
|
|
||||||
|
* Be nice: Be courteous, respectful and polite to fellow community members:
|
||||||
|
no regional, racial, gender, or other abuse will be tolerated. We like
|
||||||
|
nice people way better than mean ones!
|
||||||
|
|
||||||
|
* Encourage diversity and participation: Make everyone in our community feel
|
||||||
|
welcome, regardless of their background and the extent of their
|
||||||
|
contributions, and do everything possible to encourage participation in
|
||||||
|
our community.
|
||||||
|
|
||||||
|
* Keep it legal: Basically, don't get us in trouble. Share only content that
|
||||||
|
you own, do not share private or sensitive information, and don't break
|
||||||
|
the law.
|
||||||
|
|
||||||
|
* Stay on topic: Make sure that you are posting to the correct channel and
|
||||||
|
avoid off-topic discussions. Remember when you update an issue or respond
|
||||||
|
to an email you are potentially sending to a large number of people. Please
|
||||||
|
consider this before you update. Also remember that nobody likes spam.
|
||||||
|
|
||||||
|
* Don't send email to the maintainers: There's no need to send email to the
|
||||||
|
maintainers to ask them to investigate an issue or to take a look at a
|
||||||
|
pull request. Instead of sending an email, GitHub mentions should be
|
||||||
|
used to ping maintainers to review a pull request, a proposal or an
|
||||||
|
issue.
|
||||||
|
|
||||||
|
### Guideline violations — 3 strikes method
|
||||||
|
|
||||||
|
The point of this section is not to find opportunities to punish people, but we
|
||||||
|
do need a fair way to deal with people who are making our community suck.
|
||||||
|
|
||||||
|
1. First occurrence: We'll give you a friendly, but public reminder that the
|
||||||
|
behavior is inappropriate according to our guidelines.
|
||||||
|
|
||||||
|
2. Second occurrence: We will send you a private message with a warning that
|
||||||
|
any additional violations will result in removal from the community.
|
||||||
|
|
||||||
|
3. Third occurrence: Depending on the violation, we may need to delete or ban
|
||||||
|
your account.
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
|
||||||
|
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
|
||||||
|
have spam all over the place.
|
||||||
|
|
||||||
|
* Violations are forgiven after 6 months of good behavior, and we won't hold a
|
||||||
|
grudge.
|
||||||
|
|
||||||
|
* People who commit minor infractions will get some education, rather than
|
||||||
|
hammering them in the 3 strikes process.
|
||||||
|
|
||||||
|
* The rules apply equally to everyone in the community, no matter how much
|
||||||
|
you've contributed.
|
||||||
|
|
||||||
|
* Extreme violations of a threatening, abusive, destructive or illegal nature
|
||||||
|
will be addressed immediately and are not subject to 3 strikes or forgiveness.
|
||||||
|
|
||||||
|
* Contact [xuri.me](https://xuri.me) to report abuse or appeal violations. In the case of
|
||||||
|
appeals, we know that mistakes happen, and we'll work with you to come up with a
|
||||||
|
fair solution if there has been a misunderstanding.
|
||||||
|
|
||||||
|
## Coding Style
|
||||||
|
|
||||||
|
Unless explicitly stated, we follow all coding guidelines from the Go
|
||||||
|
community. While some of these standards may seem arbitrary, they somehow seem
|
||||||
|
to result in a solid, consistent codebase.
|
||||||
|
|
||||||
|
It is possible that the code base does not currently comply with these
|
||||||
|
guidelines. We are not looking for a massive PR that fixes this, since that
|
||||||
|
goes against the spirit of the guidelines. All new contributions should make a
|
||||||
|
best effort to clean up and make the code base better than they left it.
|
||||||
|
Obviously, apply your best judgement. Remember, the goal here is to make the
|
||||||
|
code base easier for humans to navigate and understand. Always keep that in
|
||||||
|
mind when nudging others to comply.
|
||||||
|
|
||||||
|
The rules:
|
||||||
|
|
||||||
|
1. All code should be formatted with `gofmt -s`.
|
||||||
|
2. All code should pass the default levels of
|
||||||
|
[`golint`](https://github.com/golang/lint).
|
||||||
|
3. All code should follow the guidelines covered in [Effective
|
||||||
|
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||||
|
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
4. Comment the code. Tell us the why, the history and the context.
|
||||||
|
5. Document _all_ declarations and methods, even private ones. Declare
|
||||||
|
expectations, caveats and anything else that may be important. If a type
|
||||||
|
gets exported, having the comments already there will ensure it's ready.
|
||||||
|
6. Variable name length should be proportional to its context and no longer.
|
||||||
|
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
|
||||||
|
In practice, short methods will have short variable names and globals will
|
||||||
|
have longer names.
|
||||||
|
7. No underscores in package names. If you need a compound name, step back,
|
||||||
|
and re-examine why you need a compound name. If you still think you need a
|
||||||
|
compound name, lose the underscore.
|
||||||
|
8. No utils or helpers packages. If a function is not general enough to
|
||||||
|
warrant its own package, it has not been written generally enough to be a
|
||||||
|
part of a util package. Just leave it unexported and well-documented.
|
||||||
|
9. All tests should run with `go test` and outside tooling should not be
|
||||||
|
required. No, we don't need another unit testing framework. Assertion
|
||||||
|
packages are acceptable if they provide _real_ incremental value.
|
||||||
|
10. Even though we call these "rules" above, they are actually just
|
||||||
|
guidelines. Since you've read all the rules, you now know that.
|
||||||
|
|
||||||
|
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||||
|
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||||
|
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||||
|
kool-aid is a lot easier than going thirsty.
|
||||||
|
|
||||||
|
## Code Review Comments and Effective Go Guidelines
|
||||||
|
[CodeLingo](https://codelingo.io) automatically checks every pull request against the following guidelines from [Effective Go](https://golang.org/doc/effective_go.html) and [Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
|
||||||
|
{{range .}}
|
||||||
|
### {{.title}}
|
||||||
|
{{.body}}
|
||||||
|
{{end}}
|
||||||
29
vendor/github.com/360EntSecGroup-Skylar/excelize/LICENSE
generated
vendored
Normal file
29
vendor/github.com/360EntSecGroup-Skylar/excelize/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2016 - 2019 360 Enterprise Security Group, Endpoint Security,
|
||||||
|
inc. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Excelize nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
45
vendor/github.com/360EntSecGroup-Skylar/excelize/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
45
vendor/github.com/360EntSecGroup-Skylar/excelize/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# PR Details
|
||||||
|
|
||||||
|
<!--- Provide a general summary of your changes in the Title above -->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--- Describe your changes in detail -->
|
||||||
|
|
||||||
|
## Related Issue
|
||||||
|
|
||||||
|
<!--- This project only accepts pull requests related to open issues -->
|
||||||
|
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
|
||||||
|
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
|
||||||
|
<!--- Please link to the issue here: -->
|
||||||
|
|
||||||
|
## Motivation and Context
|
||||||
|
|
||||||
|
<!--- Why is this change required? What problem does it solve? -->
|
||||||
|
|
||||||
|
## How Has This Been Tested
|
||||||
|
|
||||||
|
<!--- Please describe in detail how you tested your changes. -->
|
||||||
|
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||||
|
<!--- see how your change affects other areas of the code, etc. -->
|
||||||
|
|
||||||
|
## Types of changes
|
||||||
|
|
||||||
|
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||||
|
|
||||||
|
- [ ] Docs change / refactoring / dependency upgrade
|
||||||
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||||
|
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||||
|
|
||||||
|
- [ ] My code follows the code style of this project.
|
||||||
|
- [ ] My change requires a change to the documentation.
|
||||||
|
- [ ] I have updated the documentation accordingly.
|
||||||
|
- [ ] I have read the **CONTRIBUTING** document.
|
||||||
|
- [ ] I have added tests to cover my changes.
|
||||||
|
- [ ] All new and existing tests passed.
|
||||||
179
vendor/github.com/360EntSecGroup-Skylar/excelize/README.md
generated
vendored
Normal file
179
vendor/github.com/360EntSecGroup-Skylar/excelize/README.md
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<p align="center"><img width="650" src="./excelize.png" alt="Excelize logo"></p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
|
||||||
|
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
|
||||||
|
<a href="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize"><img src="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize?status.svg" alt="GoDoc"></a>
|
||||||
|
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
|
||||||
|
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Excelize
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Excelize is a library written in pure Go and providing a set of functions that allow you to write to and read from XLSX files. Support reads and writes XLSX file generated by Microsoft Excel™ 2007 and later. Support save file without losing original charts of XLSX. This library needs Go version 1.8 or later. The full API docs can be seen using go's built-in documentation tool, or online at [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) and [docs reference](https://xuri.me/excelize/).
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```go
|
||||||
|
go get github.com/360EntSecGroup-Skylar/excelize
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create XLSX file
|
||||||
|
|
||||||
|
Here is a minimal example usage that will create XLSX file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx := excelize.NewFile()
|
||||||
|
// Create a new sheet.
|
||||||
|
index := xlsx.NewSheet("Sheet2")
|
||||||
|
// Set value of a cell.
|
||||||
|
xlsx.SetCellValue("Sheet2", "A2", "Hello world.")
|
||||||
|
xlsx.SetCellValue("Sheet1", "B2", 100)
|
||||||
|
// Set active sheet of the workbook.
|
||||||
|
xlsx.SetActiveSheet(index)
|
||||||
|
// Save xlsx file by the given path.
|
||||||
|
err := xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reading XLSX file
|
||||||
|
|
||||||
|
The following constitutes the bare to read a XLSX document.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get value from cell by given worksheet name and axis.
|
||||||
|
cell := xlsx.GetCellValue("Sheet1", "B2")
|
||||||
|
fmt.Println(cell)
|
||||||
|
// Get all the rows in the Sheet1.
|
||||||
|
rows := xlsx.GetRows("Sheet1")
|
||||||
|
for _, row := range rows {
|
||||||
|
for _, colCell := range row {
|
||||||
|
fmt.Print(colCell, "\t")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add chart to XLSX file
|
||||||
|
|
||||||
|
With Excelize chart generation and management is as easy as a few lines of code. You can build charts based off data in your worksheet or generate charts without any data in your worksheet at all.
|
||||||
|
|
||||||
|
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
||||||
|
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
||||||
|
xlsx := excelize.NewFile()
|
||||||
|
for k, v := range categories {
|
||||||
|
xlsx.SetCellValue("Sheet1", k, v)
|
||||||
|
}
|
||||||
|
for k, v := range values {
|
||||||
|
xlsx.SetCellValue("Sheet1", k, v)
|
||||||
|
}
|
||||||
|
xlsx.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`)
|
||||||
|
// Save xlsx file by the given path.
|
||||||
|
err := xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add picture to XLSX file
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
_ "image/gif"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Insert a picture.
|
||||||
|
err = xlsx.AddPicture("Sheet1", "A2", "./image1.png", "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// Insert a picture to worksheet with scaling.
|
||||||
|
err = xlsx.AddPicture("Sheet1", "D2", "./image2.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// Insert a picture offset in the cell with printing support.
|
||||||
|
err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// Save the xlsx file with the origin path.
|
||||||
|
err = xlsx.Save()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Open a pull request to fix a bug, or open an issue to discuss a new feature or change. XML is compliant with [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm).
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Some struct of XML originally by [tealeg/xlsx](https://github.com/tealeg/xlsx).
|
||||||
|
|
||||||
|
## Licenses
|
||||||
|
|
||||||
|
This program is under the terms of the BSD 3-Clause License. See [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause).
|
||||||
|
|
||||||
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2F360EntSecGroup-Skylar%2Fexcelize?ref=badge_large)
|
||||||
179
vendor/github.com/360EntSecGroup-Skylar/excelize/README_zh.md
generated
vendored
Normal file
179
vendor/github.com/360EntSecGroup-Skylar/excelize/README_zh.md
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<p align="center"><img width="650" src="./excelize.png" alt="Excelize logo"></p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://travis-ci.org/360EntSecGroup-Skylar/excelize"><img src="https://travis-ci.org/360EntSecGroup-Skylar/excelize.svg?branch=master" alt="Build Status"></a>
|
||||||
|
<a href="https://codecov.io/gh/360EntSecGroup-Skylar/excelize"><img src="https://codecov.io/gh/360EntSecGroup-Skylar/excelize/branch/master/graph/badge.svg" alt="Code Coverage"></a>
|
||||||
|
<a href="https://goreportcard.com/report/github.com/360EntSecGroup-Skylar/excelize"><img src="https://goreportcard.com/badge/github.com/360EntSecGroup-Skylar/excelize" alt="Go Report Card"></a>
|
||||||
|
<a href="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize"><img src="https://godoc.org/github.com/360EntSecGroup-Skylar/excelize?status.svg" alt="GoDoc"></a>
|
||||||
|
<a href="https://opensource.org/licenses/BSD-3-Clause"><img src="https://img.shields.io/badge/license-bsd-orange.svg" alt="Licenses"></a>
|
||||||
|
<a href="https://www.paypal.me/xuri"><img src="https://img.shields.io/badge/Donate-PayPal-green.svg" alt="Donate"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Excelize
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office OpenXML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。使用本类库要求使用的 Go 语言为 1.8 或更高版本,完整的 API 使用文档请访问 [godoc.org](https://godoc.org/github.com/360EntSecGroup-Skylar/excelize) 或查看 [参考文档](https://xuri.me/excelize/)。
|
||||||
|
|
||||||
|
## 快速上手
|
||||||
|
|
||||||
|
### 安装
|
||||||
|
|
||||||
|
```go
|
||||||
|
go get github.com/360EntSecGroup-Skylar/excelize
|
||||||
|
```
|
||||||
|
|
||||||
|
### 创建 Excel 文档
|
||||||
|
|
||||||
|
下面是一个创建 Excel 文档的简单例子:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx := excelize.NewFile()
|
||||||
|
// 创建一个工作表
|
||||||
|
index := xlsx.NewSheet("Sheet2")
|
||||||
|
// 设置单元格的值
|
||||||
|
xlsx.SetCellValue("Sheet2", "A2", "Hello world.")
|
||||||
|
xlsx.SetCellValue("Sheet1", "B2", 100)
|
||||||
|
// 设置工作簿的默认工作表
|
||||||
|
xlsx.SetActiveSheet(index)
|
||||||
|
// 根据指定路径保存文件
|
||||||
|
err := xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 读取 Excel 文档
|
||||||
|
|
||||||
|
下面是读取 Excel 文档的例子:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取工作表中指定单元格的值
|
||||||
|
cell := xlsx.GetCellValue("Sheet1", "B2")
|
||||||
|
fmt.Println(cell)
|
||||||
|
// 获取 Sheet1 上所有单元格
|
||||||
|
rows := xlsx.GetRows("Sheet1")
|
||||||
|
for _, row := range rows {
|
||||||
|
for _, colCell := range row {
|
||||||
|
fmt.Print(colCell, "\t")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 Excel 文档中创建图表
|
||||||
|
|
||||||
|
使用 Excelize 生成图表十分简单,仅需几行代码。您可以根据工作表中的已有数据构建图表,或向工作表中添加数据并创建图表。
|
||||||
|
|
||||||
|
<p align="center"><img width="650" src="./test/images/chart.png" alt="Excelize"></p>
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
categories := map[string]string{"A2": "Small", "A3": "Normal", "A4": "Large", "B1": "Apple", "C1": "Orange", "D1": "Pear"}
|
||||||
|
values := map[string]int{"B2": 2, "C2": 3, "D2": 3, "B3": 5, "C3": 2, "D3": 4, "B4": 6, "C4": 7, "D4": 8}
|
||||||
|
xlsx := excelize.NewFile()
|
||||||
|
for k, v := range categories {
|
||||||
|
xlsx.SetCellValue("Sheet1", k, v)
|
||||||
|
}
|
||||||
|
for k, v := range values {
|
||||||
|
xlsx.SetCellValue("Sheet1", k, v)
|
||||||
|
}
|
||||||
|
xlsx.AddChart("Sheet1", "E1", `{"type":"col3DClustered","series":[{"name":"Sheet1!$A$2","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$2:$D$2"},{"name":"Sheet1!$A$3","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$3:$D$3"},{"name":"Sheet1!$A$4","categories":"Sheet1!$B$1:$D$1","values":"Sheet1!$B$4:$D$4"}],"title":{"name":"Fruit 3D Clustered Column Chart"}}`)
|
||||||
|
// 根据指定路径保存文件
|
||||||
|
err := xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 向 Excel 文档中插入图片
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
_ "image/gif"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
|
||||||
|
"github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 插入图片
|
||||||
|
err = xlsx.AddPicture("Sheet1", "A2", "./image1.png", "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// 在工作表中插入图片,并设置图片的缩放比例
|
||||||
|
err = xlsx.AddPicture("Sheet1", "D2", "./image2.jpg", `{"x_scale": 0.5, "y_scale": 0.5}`)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// 在工作表中插入图片,并设置图片的打印属性
|
||||||
|
err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "print_obj": true, "lock_aspect_ratio": false, "locked": false}`)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
// 保存文件
|
||||||
|
err = xlsx.Save()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 社区合作
|
||||||
|
|
||||||
|
欢迎您为此项目贡献代码,提出建议或问题、修复 Bug 以及参与讨论对新功能的想法。 XML 符合标准: [part 1 of the 5th edition of the ECMA-376 Standard for Office Open XML](http://www.ecma-international.org/publications/standards/Ecma-376.htm)。
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
本类库中部分 XML 结构体的定义参考了开源项目:[tealeg/xlsx](https://github.com/tealeg/xlsx).
|
||||||
|
|
||||||
|
## 开源许可
|
||||||
|
|
||||||
|
本项目遵循 BSD 3-Clause 开源许可协议,访问 [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 查看许可协议文件。
|
||||||
|
|
||||||
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2F360EntSecGroup-Skylar%2Fexcelize?ref=badge_large)
|
||||||
599
vendor/github.com/360EntSecGroup-Skylar/excelize/cell.go
generated
vendored
Normal file
599
vendor/github.com/360EntSecGroup-Skylar/excelize/cell.go
generated
vendored
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// STCellFormulaTypeArray defined the formula is an array formula.
|
||||||
|
STCellFormulaTypeArray = "array"
|
||||||
|
// STCellFormulaTypeDataTable defined the formula is a data table formula.
|
||||||
|
STCellFormulaTypeDataTable = "dataTable"
|
||||||
|
// STCellFormulaTypeNormal defined the formula is a regular cell formula.
|
||||||
|
STCellFormulaTypeNormal = "normal"
|
||||||
|
// STCellFormulaTypeShared defined the formula is part of a shared formula.
|
||||||
|
STCellFormulaTypeShared = "shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mergeCellsParser provides a function to check merged cells in worksheet by
|
||||||
|
// given axis.
|
||||||
|
func (f *File) mergeCellsParser(xlsx *xlsxWorksheet, axis string) string {
|
||||||
|
axis = strings.ToUpper(axis)
|
||||||
|
if xlsx.MergeCells != nil {
|
||||||
|
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||||
|
if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
|
||||||
|
axis = strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return axis
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellValue provides a function to set value of a cell. The following
|
||||||
|
// shows the supported data types:
|
||||||
|
//
|
||||||
|
// int
|
||||||
|
// int8
|
||||||
|
// int16
|
||||||
|
// int32
|
||||||
|
// int64
|
||||||
|
// uint
|
||||||
|
// uint8
|
||||||
|
// uint16
|
||||||
|
// uint32
|
||||||
|
// uint64
|
||||||
|
// float32
|
||||||
|
// float64
|
||||||
|
// string
|
||||||
|
// []byte
|
||||||
|
// time.Duration
|
||||||
|
// time.Time
|
||||||
|
// bool
|
||||||
|
// nil
|
||||||
|
//
|
||||||
|
// Note that default date format is m/d/yy h:mm of time.Time type value. You can
|
||||||
|
// set numbers format by SetCellStyle() method.
|
||||||
|
func (f *File) SetCellValue(sheet, axis string, value interface{}) {
|
||||||
|
switch t := value.(type) {
|
||||||
|
case float32:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float32)), 'f', -1, 32))
|
||||||
|
case float64:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(float64)), 'f', -1, 64))
|
||||||
|
case string:
|
||||||
|
f.SetCellStr(sheet, axis, t)
|
||||||
|
case []byte:
|
||||||
|
f.SetCellStr(sheet, axis, string(t))
|
||||||
|
case time.Duration:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(value.(time.Duration).Seconds()/86400), 'f', -1, 32))
|
||||||
|
f.setDefaultTimeStyle(sheet, axis, 21)
|
||||||
|
case time.Time:
|
||||||
|
f.SetCellDefault(sheet, axis, strconv.FormatFloat(float64(timeToExcelTime(timeToUTCTime(value.(time.Time)))), 'f', -1, 64))
|
||||||
|
f.setDefaultTimeStyle(sheet, axis, 22)
|
||||||
|
case nil:
|
||||||
|
f.SetCellStr(sheet, axis, "")
|
||||||
|
case bool:
|
||||||
|
f.SetCellBool(sheet, axis, bool(value.(bool)))
|
||||||
|
default:
|
||||||
|
f.setCellIntValue(sheet, axis, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCellIntValue provides a function to set int value of a cell.
|
||||||
|
func (f *File) setCellIntValue(sheet, axis string, value interface{}) {
|
||||||
|
switch value.(type) {
|
||||||
|
case int:
|
||||||
|
f.SetCellInt(sheet, axis, value.(int))
|
||||||
|
case int8:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int8)))
|
||||||
|
case int16:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int16)))
|
||||||
|
case int32:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int32)))
|
||||||
|
case int64:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(int64)))
|
||||||
|
case uint:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(uint)))
|
||||||
|
case uint8:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(uint8)))
|
||||||
|
case uint16:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(uint16)))
|
||||||
|
case uint32:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(uint32)))
|
||||||
|
case uint64:
|
||||||
|
f.SetCellInt(sheet, axis, int(value.(uint64)))
|
||||||
|
default:
|
||||||
|
f.SetCellStr(sheet, axis, fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellBool provides a function to set bool type value of a cell by given
|
||||||
|
// worksheet name, cell coordinates and cell value.
|
||||||
|
func (f *File) SetCellBool(sheet, axis string, value bool) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = "b"
|
||||||
|
if value {
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = "1"
|
||||||
|
} else {
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCellValue provides a function to get formatted value from cell by given
|
||||||
|
// worksheet name and axis in XLSX file. If it is possible to apply a format
|
||||||
|
// to the cell value, it will do so, if not then an error will be returned,
|
||||||
|
// along with the raw value of the cell.
|
||||||
|
func (f *File) GetCellValue(sheet, axis string) string {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
rows := len(xlsx.SheetData.Row)
|
||||||
|
if rows > 1 {
|
||||||
|
lastRow := xlsx.SheetData.Row[rows-1].R
|
||||||
|
if lastRow >= rows {
|
||||||
|
rows = lastRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rows < xAxis {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for k := range xlsx.SheetData.Row {
|
||||||
|
if xlsx.SheetData.Row[k].R == row {
|
||||||
|
for i := range xlsx.SheetData.Row[k].C {
|
||||||
|
if axis == xlsx.SheetData.Row[k].C[i].R {
|
||||||
|
val, _ := xlsx.SheetData.Row[k].C[i].getValueFrom(f, f.sharedStringsReader())
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// formattedValue provides a function to returns a value after formatted. If
|
||||||
|
// it is possible to apply a format to the cell value, it will do so, if not
|
||||||
|
// then an error will be returned, along with the raw value of the cell.
|
||||||
|
func (f *File) formattedValue(s int, v string) string {
|
||||||
|
if s == 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
styleSheet := f.stylesReader()
|
||||||
|
ok := builtInNumFmtFunc[styleSheet.CellXfs.Xf[s].NumFmtID]
|
||||||
|
if ok != nil {
|
||||||
|
return ok(styleSheet.CellXfs.Xf[s].NumFmtID, v)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCellStyle provides a function to get cell style index by given worksheet
|
||||||
|
// name and cell coordinates.
|
||||||
|
func (f *File) GetCellStyle(sheet, axis string) int {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
return f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCellFormula provides a function to get formula from cell by given
|
||||||
|
// worksheet name and axis in XLSX file.
|
||||||
|
func (f *File) GetCellFormula(sheet, axis string) string {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
rows := len(xlsx.SheetData.Row)
|
||||||
|
if rows > 1 {
|
||||||
|
lastRow := xlsx.SheetData.Row[rows-1].R
|
||||||
|
if lastRow >= rows {
|
||||||
|
rows = lastRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rows < xAxis {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for k := range xlsx.SheetData.Row {
|
||||||
|
if xlsx.SheetData.Row[k].R == row {
|
||||||
|
for i := range xlsx.SheetData.Row[k].C {
|
||||||
|
if axis == xlsx.SheetData.Row[k].C[i].R {
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F.T == STCellFormulaTypeShared {
|
||||||
|
return getSharedForumula(xlsx, xlsx.SheetData.Row[k].C[i].F.Si)
|
||||||
|
}
|
||||||
|
return xlsx.SheetData.Row[k].C[i].F.Content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSharedForumula find a cell contains the same formula as another cell,
|
||||||
|
// the "shared" value can be used for the t attribute and the si attribute can
|
||||||
|
// be used to refer to the cell containing the formula. Two formulas are
|
||||||
|
// considered to be the same when their respective representations in
|
||||||
|
// R1C1-reference notation, are the same.
|
||||||
|
//
|
||||||
|
// Note that this function not validate ref tag to check the cell if or not in
|
||||||
|
// allow area, and always return origin shared formula.
|
||||||
|
func getSharedForumula(xlsx *xlsxWorksheet, si string) string {
|
||||||
|
for k := range xlsx.SheetData.Row {
|
||||||
|
for i := range xlsx.SheetData.Row[k].C {
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F.T != STCellFormulaTypeShared {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F.Si != si {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if xlsx.SheetData.Row[k].C[i].F.Ref != "" {
|
||||||
|
return xlsx.SheetData.Row[k].C[i].F.Content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellFormula provides a function to set cell formula by given string and
|
||||||
|
// worksheet name.
|
||||||
|
func (f *File) SetCellFormula(sheet, axis, formula string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
if xlsx.SheetData.Row[xAxis].C[yAxis].F != nil {
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].F.Content = formula
|
||||||
|
} else {
|
||||||
|
f := xlsxF{
|
||||||
|
Content: formula,
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].F = &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellHyperLink provides a function to set cell hyperlink by given
|
||||||
|
// worksheet name and link URL address. LinkType defines two types of
|
||||||
|
// hyperlink "External" for web site or "Location" for moving to one of cell
|
||||||
|
// in this workbook. The below is example for external link.
|
||||||
|
//
|
||||||
|
// xlsx.SetCellHyperLink("Sheet1", "A3", "https://github.com/360EntSecGroup-Skylar/excelize", "External")
|
||||||
|
// // Set underline and font color style for the cell.
|
||||||
|
// style, _ := xlsx.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
|
||||||
|
// xlsx.SetCellStyle("Sheet1", "A3", "A3", style)
|
||||||
|
//
|
||||||
|
// A this is another example for "Location":
|
||||||
|
//
|
||||||
|
// xlsx.SetCellHyperLink("Sheet1", "A3", "Sheet1!A40", "Location")
|
||||||
|
//
|
||||||
|
func (f *File) SetCellHyperLink(sheet, axis, link, linkType string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
linkTypes := map[string]xlsxHyperlink{
|
||||||
|
"External": {},
|
||||||
|
"Location": {Location: link},
|
||||||
|
}
|
||||||
|
hyperlink, ok := linkTypes[linkType]
|
||||||
|
if !ok || axis == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hyperlink.Ref = axis
|
||||||
|
if linkType == "External" {
|
||||||
|
rID := f.addSheetRelationships(sheet, SourceRelationshipHyperLink, link, linkType)
|
||||||
|
hyperlink.RID = "rId" + strconv.Itoa(rID)
|
||||||
|
}
|
||||||
|
if xlsx.Hyperlinks == nil {
|
||||||
|
xlsx.Hyperlinks = &xlsxHyperlinks{}
|
||||||
|
}
|
||||||
|
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink, hyperlink)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCellHyperLink provides a function to get cell hyperlink by given
|
||||||
|
// worksheet name and axis. Boolean type value link will be ture if the cell
|
||||||
|
// has a hyperlink and the target is the address of the hyperlink. Otherwise,
|
||||||
|
// the value of link will be false and the value of the target will be a blank
|
||||||
|
// string. For example get hyperlink of Sheet1!H6:
|
||||||
|
//
|
||||||
|
// link, target := xlsx.GetCellHyperLink("Sheet1", "H6")
|
||||||
|
//
|
||||||
|
func (f *File) GetCellHyperLink(sheet, axis string) (bool, string) {
|
||||||
|
var link bool
|
||||||
|
var target string
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
if xlsx.Hyperlinks == nil || axis == "" {
|
||||||
|
return link, target
|
||||||
|
}
|
||||||
|
for h := range xlsx.Hyperlinks.Hyperlink {
|
||||||
|
if xlsx.Hyperlinks.Hyperlink[h].Ref == axis {
|
||||||
|
link = true
|
||||||
|
target = xlsx.Hyperlinks.Hyperlink[h].Location
|
||||||
|
if xlsx.Hyperlinks.Hyperlink[h].RID != "" {
|
||||||
|
target = f.getSheetRelationshipsTargetByID(sheet, xlsx.Hyperlinks.Hyperlink[h].RID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return link, target
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeCell provides a function to merge cells by given coordinate area and
|
||||||
|
// sheet name. For example create a merged cell of D3:E9 on Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.MergeCell("Sheet1", "D3", "E9")
|
||||||
|
//
|
||||||
|
// If you create a merged cell that overlaps with another existing merged cell,
|
||||||
|
// those merged cells that already exist will be removed.
|
||||||
|
func (f *File) MergeCell(sheet, hcell, vcell string) {
|
||||||
|
if hcell == vcell {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hcell = strings.ToUpper(hcell)
|
||||||
|
vcell = strings.ToUpper(vcell)
|
||||||
|
|
||||||
|
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||||
|
hcol := string(strings.Map(letterOnlyMapF, hcell))
|
||||||
|
hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
|
||||||
|
hyAxis := hrow - 1
|
||||||
|
hxAxis := TitleToNumber(hcol)
|
||||||
|
|
||||||
|
vcol := string(strings.Map(letterOnlyMapF, vcell))
|
||||||
|
vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
|
||||||
|
vyAxis := vrow - 1
|
||||||
|
vxAxis := TitleToNumber(vcol)
|
||||||
|
|
||||||
|
if vxAxis < hxAxis {
|
||||||
|
hcell, vcell = vcell, hcell
|
||||||
|
vxAxis, hxAxis = hxAxis, vxAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
if vyAxis < hyAxis {
|
||||||
|
hcell, vcell = vcell, hcell
|
||||||
|
vyAxis, hyAxis = hyAxis, vyAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.MergeCells != nil {
|
||||||
|
mergeCell := xlsxMergeCell{}
|
||||||
|
// Correct the coordinate area, such correct C1:B3 to B1:C3.
|
||||||
|
mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||||
|
// Delete the merged cells of the overlapping area.
|
||||||
|
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||||
|
if checkCellInArea(hcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[0], mergeCell.Ref) {
|
||||||
|
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
|
||||||
|
} else if checkCellInArea(vcell, xlsx.MergeCells.Cells[i].Ref) || checkCellInArea(strings.Split(xlsx.MergeCells.Cells[i].Ref, ":")[1], mergeCell.Ref) {
|
||||||
|
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:i], xlsx.MergeCells.Cells[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells, &mergeCell)
|
||||||
|
} else {
|
||||||
|
mergeCell := xlsxMergeCell{}
|
||||||
|
// Correct the coordinate area, such correct C1:B3 to B1:C3.
|
||||||
|
mergeCell.Ref = ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||||
|
mergeCells := xlsxMergeCells{}
|
||||||
|
mergeCells.Cells = append(mergeCells.Cells, &mergeCell)
|
||||||
|
xlsx.MergeCells = &mergeCells
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellInt provides a function to set int type value of a cell by given
|
||||||
|
// worksheet name, cell coordinates and cell value.
|
||||||
|
func (f *File) SetCellInt(sheet, axis string, value int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = strconv.Itoa(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCellStyle provides a function to prepare style index of cell in
|
||||||
|
// worksheet by given column index and style index.
|
||||||
|
func (f *File) prepareCellStyle(xlsx *xlsxWorksheet, col, style int) int {
|
||||||
|
if xlsx.Cols != nil && style == 0 {
|
||||||
|
for _, v := range xlsx.Cols.Col {
|
||||||
|
if v.Min <= col && col <= v.Max {
|
||||||
|
style = v.Style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return style
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellStr provides a function to set string type value of a cell. Total
|
||||||
|
// number of characters that a cell can contain 32767 characters.
|
||||||
|
func (f *File) SetCellStr(sheet, axis, value string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
if len(value) > 32767 {
|
||||||
|
value = value[0:32767]
|
||||||
|
}
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
// Leading space(s) character detection.
|
||||||
|
if len(value) > 0 {
|
||||||
|
if value[0] == 32 {
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].XMLSpace = xml.Attr{
|
||||||
|
Name: xml.Name{Space: NameSpaceXML, Local: "space"},
|
||||||
|
Value: "preserve",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = "str"
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCellDefault provides a function to set string type value of a cell as
|
||||||
|
// default format without escaping the cell.
|
||||||
|
func (f *File) SetCellDefault(sheet, axis, value string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].S = f.prepareCellStyle(xlsx, cell, xlsx.SheetData.Row[xAxis].C[yAxis].S)
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].T = ""
|
||||||
|
xlsx.SheetData.Row[xAxis].C[yAxis].V = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetRow writes an array to row by given worksheet name, starting
|
||||||
|
// coordinate and a pointer to array type 'slice'. For example, writes an
|
||||||
|
// array to row 6 start with the cell B6 on Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetSheetRow("Sheet1", "B6", &[]interface{}{"1", nil, 2})
|
||||||
|
//
|
||||||
|
func (f *File) SetSheetRow(sheet, axis string, slice interface{}) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
axis = f.mergeCellsParser(xlsx, axis)
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, err := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Make sure 'slice' is a Ptr to Slice
|
||||||
|
v := reflect.ValueOf(slice)
|
||||||
|
if v.Kind() != reflect.Ptr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
if v.Kind() != reflect.Slice {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
|
||||||
|
rows := xAxis + 1
|
||||||
|
cell := yAxis + 1
|
||||||
|
|
||||||
|
completeRow(xlsx, rows, cell)
|
||||||
|
completeCol(xlsx, rows, cell)
|
||||||
|
|
||||||
|
idx := 0
|
||||||
|
for i := cell - 1; i < v.Len()+cell-1; i++ {
|
||||||
|
c := ToAlphaString(i) + strconv.Itoa(row)
|
||||||
|
f.SetCellValue(sheet, c, v.Index(idx).Interface())
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkCellInArea provides a function to determine if a given coordinate is
|
||||||
|
// within an area.
|
||||||
|
func checkCellInArea(cell, area string) bool {
|
||||||
|
cell = strings.ToUpper(cell)
|
||||||
|
area = strings.ToUpper(area)
|
||||||
|
|
||||||
|
ref := strings.Split(area, ":")
|
||||||
|
if len(ref) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
from := ref[0]
|
||||||
|
to := ref[1]
|
||||||
|
|
||||||
|
col, row := getCellColRow(cell)
|
||||||
|
fromCol, fromRow := getCellColRow(from)
|
||||||
|
toCol, toRow := getCellColRow(to)
|
||||||
|
|
||||||
|
return axisLowerOrEqualThan(fromCol, col) && axisLowerOrEqualThan(col, toCol) && axisLowerOrEqualThan(fromRow, row) && axisLowerOrEqualThan(row, toRow)
|
||||||
|
}
|
||||||
1291
vendor/github.com/360EntSecGroup-Skylar/excelize/chart.go
generated
vendored
Normal file
1291
vendor/github.com/360EntSecGroup-Skylar/excelize/chart.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
vendor/github.com/360EntSecGroup-Skylar/excelize/codelingo.yaml
generated
vendored
Normal file
3
vendor/github.com/360EntSecGroup-Skylar/excelize/codelingo.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
tenets:
|
||||||
|
- import: codelingo/effective-go
|
||||||
|
- import: codelingo/code-review-comments
|
||||||
376
vendor/github.com/360EntSecGroup-Skylar/excelize/col.go
generated
vendored
Normal file
376
vendor/github.com/360EntSecGroup-Skylar/excelize/col.go
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define the default cell size and EMU unit of measurement.
|
||||||
|
const (
|
||||||
|
defaultColWidthPixels float64 = 64
|
||||||
|
defaultRowHeightPixels float64 = 20
|
||||||
|
EMU int = 9525
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetColVisible provides a function to get visible of a single column by given
|
||||||
|
// worksheet name and column name. For example, get visible state of column D
|
||||||
|
// in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetColVisible("Sheet1", "D")
|
||||||
|
//
|
||||||
|
func (f *File) GetColVisible(sheet, column string) bool {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||||
|
visible := true
|
||||||
|
if xlsx.Cols == nil {
|
||||||
|
return visible
|
||||||
|
}
|
||||||
|
for c := range xlsx.Cols.Col {
|
||||||
|
if xlsx.Cols.Col[c].Min <= col && col <= xlsx.Cols.Col[c].Max {
|
||||||
|
visible = !xlsx.Cols.Col[c].Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visible
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColVisible provides a function to set visible of a single column by given
|
||||||
|
// worksheet name and column name. For example, hide column D in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetColVisible("Sheet1", "D", false)
|
||||||
|
//
|
||||||
|
func (f *File) SetColVisible(sheet, column string, visible bool) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
c := TitleToNumber(strings.ToUpper(column)) + 1
|
||||||
|
col := xlsxCol{
|
||||||
|
Min: c,
|
||||||
|
Max: c,
|
||||||
|
Hidden: !visible,
|
||||||
|
CustomWidth: true,
|
||||||
|
}
|
||||||
|
if xlsx.Cols == nil {
|
||||||
|
cols := xlsxCols{}
|
||||||
|
cols.Col = append(cols.Col, col)
|
||||||
|
xlsx.Cols = &cols
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for v := range xlsx.Cols.Col {
|
||||||
|
if xlsx.Cols.Col[v].Min <= c && c <= xlsx.Cols.Col[v].Max {
|
||||||
|
col = xlsx.Cols.Col[v]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col.Min = c
|
||||||
|
col.Max = c
|
||||||
|
col.Hidden = !visible
|
||||||
|
col.CustomWidth = true
|
||||||
|
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetColOutlineLevel provides a function to get outline level of a single
|
||||||
|
// column by given worksheet name and column name. For example, get outline
|
||||||
|
// level of column D in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetColOutlineLevel("Sheet1", "D")
|
||||||
|
//
|
||||||
|
func (f *File) GetColOutlineLevel(sheet, column string) uint8 {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||||
|
level := uint8(0)
|
||||||
|
if xlsx.Cols == nil {
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
for c := range xlsx.Cols.Col {
|
||||||
|
if xlsx.Cols.Col[c].Min <= col && col <= xlsx.Cols.Col[c].Max {
|
||||||
|
level = xlsx.Cols.Col[c].OutlineLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColOutlineLevel provides a function to set outline level of a single
|
||||||
|
// column by given worksheet name and column name. For example, set outline
|
||||||
|
// level of column D in Sheet1 to 2:
|
||||||
|
//
|
||||||
|
// xlsx.SetColOutlineLevel("Sheet1", "D", 2)
|
||||||
|
//
|
||||||
|
func (f *File) SetColOutlineLevel(sheet, column string, level uint8) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
c := TitleToNumber(strings.ToUpper(column)) + 1
|
||||||
|
col := xlsxCol{
|
||||||
|
Min: c,
|
||||||
|
Max: c,
|
||||||
|
OutlineLevel: level,
|
||||||
|
CustomWidth: true,
|
||||||
|
}
|
||||||
|
if xlsx.Cols == nil {
|
||||||
|
cols := xlsxCols{}
|
||||||
|
cols.Col = append(cols.Col, col)
|
||||||
|
xlsx.Cols = &cols
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for v := range xlsx.Cols.Col {
|
||||||
|
if xlsx.Cols.Col[v].Min <= c && c <= xlsx.Cols.Col[v].Max {
|
||||||
|
col = xlsx.Cols.Col[v]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col.Min = c
|
||||||
|
col.Max = c
|
||||||
|
col.OutlineLevel = level
|
||||||
|
col.CustomWidth = true
|
||||||
|
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColWidth provides a function to set the width of a single column or
|
||||||
|
// multiple columns. For example:
|
||||||
|
//
|
||||||
|
// xlsx := excelize.NewFile()
|
||||||
|
// xlsx.SetColWidth("Sheet1", "A", "H", 20)
|
||||||
|
// err := xlsx.Save()
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) SetColWidth(sheet, startcol, endcol string, width float64) {
|
||||||
|
min := TitleToNumber(strings.ToUpper(startcol)) + 1
|
||||||
|
max := TitleToNumber(strings.ToUpper(endcol)) + 1
|
||||||
|
if min > max {
|
||||||
|
min, max = max, min
|
||||||
|
}
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
col := xlsxCol{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
Width: width,
|
||||||
|
CustomWidth: true,
|
||||||
|
}
|
||||||
|
if xlsx.Cols != nil {
|
||||||
|
xlsx.Cols.Col = append(xlsx.Cols.Col, col)
|
||||||
|
} else {
|
||||||
|
cols := xlsxCols{}
|
||||||
|
cols.Col = append(cols.Col, col)
|
||||||
|
xlsx.Cols = &cols
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// positionObjectPixels calculate the vertices that define the position of a
|
||||||
|
// graphical object within the worksheet in pixels.
|
||||||
|
//
|
||||||
|
// +------------+------------+
|
||||||
|
// | A | B |
|
||||||
|
// +-----+------------+------------+
|
||||||
|
// | |(x1,y1) | |
|
||||||
|
// | 1 |(A1)._______|______ |
|
||||||
|
// | | | | |
|
||||||
|
// | | | | |
|
||||||
|
// +-----+----| OBJECT |-----+
|
||||||
|
// | | | | |
|
||||||
|
// | 2 | |______________. |
|
||||||
|
// | | | (B2)|
|
||||||
|
// | | | (x2,y2)|
|
||||||
|
// +-----+------------+------------+
|
||||||
|
//
|
||||||
|
// Example of an object that covers some of the area from cell A1 to B2.
|
||||||
|
//
|
||||||
|
// Based on the width and height of the object we need to calculate 8 vars:
|
||||||
|
//
|
||||||
|
// colStart, rowStart, colEnd, rowEnd, x1, y1, x2, y2.
|
||||||
|
//
|
||||||
|
// We also calculate the absolute x and y position of the top left vertex of
|
||||||
|
// the object. This is required for images.
|
||||||
|
//
|
||||||
|
// The width and height of the cells that the object occupies can be
|
||||||
|
// variable and have to be taken into account.
|
||||||
|
//
|
||||||
|
// The values of col_start and row_start are passed in from the calling
|
||||||
|
// function. The values of col_end and row_end are calculated by
|
||||||
|
// subtracting the width and height of the object from the width and
|
||||||
|
// height of the underlying cells.
|
||||||
|
//
|
||||||
|
// colStart # Col containing upper left corner of object.
|
||||||
|
// x1 # Distance to left side of object.
|
||||||
|
//
|
||||||
|
// rowStart # Row containing top left corner of object.
|
||||||
|
// y1 # Distance to top of object.
|
||||||
|
//
|
||||||
|
// colEnd # Col containing lower right corner of object.
|
||||||
|
// x2 # Distance to right side of object.
|
||||||
|
//
|
||||||
|
// rowEnd # Row containing bottom right corner of object.
|
||||||
|
// y2 # Distance to bottom of object.
|
||||||
|
//
|
||||||
|
// width # Width of object frame.
|
||||||
|
// height # Height of object frame.
|
||||||
|
//
|
||||||
|
// xAbs # Absolute distance to left side of object.
|
||||||
|
// yAbs # Absolute distance to top side of object.
|
||||||
|
//
|
||||||
|
func (f *File) positionObjectPixels(sheet string, colStart, rowStart, x1, y1, width, height int) (int, int, int, int, int, int, int, int) {
|
||||||
|
xAbs := 0
|
||||||
|
yAbs := 0
|
||||||
|
|
||||||
|
// Calculate the absolute x offset of the top-left vertex.
|
||||||
|
for colID := 1; colID <= colStart; colID++ {
|
||||||
|
xAbs += f.getColWidth(sheet, colID)
|
||||||
|
}
|
||||||
|
xAbs += x1
|
||||||
|
|
||||||
|
// Calculate the absolute y offset of the top-left vertex.
|
||||||
|
// Store the column change to allow optimisations.
|
||||||
|
for rowID := 1; rowID <= rowStart; rowID++ {
|
||||||
|
yAbs += f.getRowHeight(sheet, rowID)
|
||||||
|
}
|
||||||
|
yAbs += y1
|
||||||
|
|
||||||
|
// Adjust start column for offsets that are greater than the col width.
|
||||||
|
for x1 >= f.getColWidth(sheet, colStart) {
|
||||||
|
x1 -= f.getColWidth(sheet, colStart)
|
||||||
|
colStart++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust start row for offsets that are greater than the row height.
|
||||||
|
for y1 >= f.getRowHeight(sheet, rowStart) {
|
||||||
|
y1 -= f.getRowHeight(sheet, rowStart)
|
||||||
|
rowStart++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise end cell to the same as the start cell.
|
||||||
|
colEnd := colStart
|
||||||
|
rowEnd := rowStart
|
||||||
|
|
||||||
|
width += x1
|
||||||
|
height += y1
|
||||||
|
|
||||||
|
// Subtract the underlying cell widths to find end cell of the object.
|
||||||
|
for width >= f.getColWidth(sheet, colEnd) {
|
||||||
|
colEnd++
|
||||||
|
width -= f.getColWidth(sheet, colEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract the underlying cell heights to find end cell of the object.
|
||||||
|
for height >= f.getRowHeight(sheet, rowEnd) {
|
||||||
|
rowEnd++
|
||||||
|
height -= f.getRowHeight(sheet, rowEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The end vertices are whatever is left from the width and height.
|
||||||
|
x2 := width
|
||||||
|
y2 := height
|
||||||
|
return colStart, rowStart, xAbs, yAbs, colEnd, rowEnd, x2, y2
|
||||||
|
}
|
||||||
|
|
||||||
|
// getColWidth provides a function to get column width in pixels by given
|
||||||
|
// sheet name and column index.
|
||||||
|
func (f *File) getColWidth(sheet string, col int) int {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.Cols != nil {
|
||||||
|
var width float64
|
||||||
|
for _, v := range xlsx.Cols.Col {
|
||||||
|
if v.Min <= col && col <= v.Max {
|
||||||
|
width = v.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if width != 0 {
|
||||||
|
return int(convertColWidthToPixels(width))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Optimisation for when the column widths haven't changed.
|
||||||
|
return int(defaultColWidthPixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetColWidth provides a function to get column width by given worksheet name
|
||||||
|
// and column index.
|
||||||
|
func (f *File) GetColWidth(sheet, column string) float64 {
|
||||||
|
col := TitleToNumber(strings.ToUpper(column)) + 1
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.Cols != nil {
|
||||||
|
var width float64
|
||||||
|
for _, v := range xlsx.Cols.Col {
|
||||||
|
if v.Min <= col && col <= v.Max {
|
||||||
|
width = v.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if width != 0 {
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Optimisation for when the column widths haven't changed.
|
||||||
|
return defaultColWidthPixels
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertCol provides a function to insert a new column before given column
|
||||||
|
// index. For example, create a new column before column C in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.InsertCol("Sheet1", "C")
|
||||||
|
//
|
||||||
|
func (f *File) InsertCol(sheet, column string) {
|
||||||
|
col := TitleToNumber(strings.ToUpper(column))
|
||||||
|
f.adjustHelper(sheet, col, -1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCol provides a function to remove single column by given worksheet
|
||||||
|
// name and column index. For example, remove column C in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.RemoveCol("Sheet1", "C")
|
||||||
|
//
|
||||||
|
func (f *File) RemoveCol(sheet, column string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
for r := range xlsx.SheetData.Row {
|
||||||
|
for k, v := range xlsx.SheetData.Row[r].C {
|
||||||
|
axis := v.R
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
if col == column {
|
||||||
|
xlsx.SheetData.Row[r].C = append(xlsx.SheetData.Row[r].C[:k], xlsx.SheetData.Row[r].C[k+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
col := TitleToNumber(strings.ToUpper(column))
|
||||||
|
f.adjustHelper(sheet, col, -1, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// completeCol provieds function to completion column element tags of XML in a
|
||||||
|
// sheet.
|
||||||
|
func completeCol(xlsx *xlsxWorksheet, row, cell int) {
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for r := range xlsx.SheetData.Row {
|
||||||
|
if len(xlsx.SheetData.Row[r].C) < cell {
|
||||||
|
start := len(xlsx.SheetData.Row[r].C)
|
||||||
|
for iii := start; iii < cell; iii++ {
|
||||||
|
buffer.WriteString(ToAlphaString(iii))
|
||||||
|
buffer.WriteString(strconv.Itoa(r + 1))
|
||||||
|
xlsx.SheetData.Row[r].C = append(xlsx.SheetData.Row[r].C, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertColWidthToPixels provieds function to convert the width of a cell
|
||||||
|
// from user's units to pixels. Excel rounds the column width to the nearest
|
||||||
|
// pixel. If the width hasn't been set by the user we use the default value.
|
||||||
|
// If the column is hidden it has a value of zero.
|
||||||
|
func convertColWidthToPixels(width float64) float64 {
|
||||||
|
var padding float64 = 5
|
||||||
|
var pixels float64
|
||||||
|
var maxDigitWidth float64 = 7
|
||||||
|
if width == 0 {
|
||||||
|
return pixels
|
||||||
|
}
|
||||||
|
if width < 1 {
|
||||||
|
pixels = (width * 12) + 0.5
|
||||||
|
return math.Ceil(pixels)
|
||||||
|
}
|
||||||
|
pixels = (width*maxDigitWidth + 0.5) + padding
|
||||||
|
return math.Ceil(pixels)
|
||||||
|
}
|
||||||
273
vendor/github.com/360EntSecGroup-Skylar/excelize/comment.go
generated
vendored
Normal file
273
vendor/github.com/360EntSecGroup-Skylar/excelize/comment.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseFormatCommentsSet provides a function to parse the format settings of
|
||||||
|
// the comment with default value.
|
||||||
|
func parseFormatCommentsSet(formatSet string) (*formatComment, error) {
|
||||||
|
format := formatComment{
|
||||||
|
Author: "Author:",
|
||||||
|
Text: " ",
|
||||||
|
}
|
||||||
|
err := json.Unmarshal([]byte(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetComments retrieves all comments and returns a map of worksheet name to
|
||||||
|
// the worksheet comments.
|
||||||
|
func (f *File) GetComments() (comments map[string][]Comment) {
|
||||||
|
comments = map[string][]Comment{}
|
||||||
|
for n := range f.sheetMap {
|
||||||
|
commentID := f.GetSheetIndex(n)
|
||||||
|
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
|
||||||
|
c, ok := f.XLSX[commentsXML]
|
||||||
|
if ok {
|
||||||
|
d := xlsxComments{}
|
||||||
|
xml.Unmarshal([]byte(c), &d)
|
||||||
|
sheetComments := []Comment{}
|
||||||
|
for _, comment := range d.CommentList.Comment {
|
||||||
|
sheetComment := Comment{}
|
||||||
|
if comment.AuthorID < len(d.Authors) {
|
||||||
|
sheetComment.Author = d.Authors[comment.AuthorID].Author
|
||||||
|
}
|
||||||
|
sheetComment.Ref = comment.Ref
|
||||||
|
sheetComment.AuthorID = comment.AuthorID
|
||||||
|
for _, text := range comment.Text.R {
|
||||||
|
sheetComment.Text += text.T
|
||||||
|
}
|
||||||
|
sheetComments = append(sheetComments, sheetComment)
|
||||||
|
}
|
||||||
|
comments[n] = sheetComments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddComment provides the method to add comment in a sheet by given worksheet
|
||||||
|
// index, cell and format set (such as author and text). Note that the max
|
||||||
|
// author length is 255 and the max text length is 32512. For example, add a
|
||||||
|
// comment in Sheet1!$A$30:
|
||||||
|
//
|
||||||
|
// xlsx.AddComment("Sheet1", "A30", `{"author":"Excelize: ","text":"This is a comment."}`)
|
||||||
|
//
|
||||||
|
func (f *File) AddComment(sheet, cell, format string) error {
|
||||||
|
formatSet, err := parseFormatCommentsSet(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Read sheet data.
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
commentID := f.countComments() + 1
|
||||||
|
drawingVML := "xl/drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
|
||||||
|
sheetRelationshipsComments := "../comments" + strconv.Itoa(commentID) + ".xml"
|
||||||
|
sheetRelationshipsDrawingVML := "../drawings/vmlDrawing" + strconv.Itoa(commentID) + ".vml"
|
||||||
|
if xlsx.LegacyDrawing != nil {
|
||||||
|
// The worksheet already has a comments relationships, use the relationships drawing ../drawings/vmlDrawing%d.vml.
|
||||||
|
sheetRelationshipsDrawingVML = f.getSheetRelationshipsTargetByID(sheet, xlsx.LegacyDrawing.RID)
|
||||||
|
commentID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingVML, "../drawings/vmlDrawing"), ".vml"))
|
||||||
|
drawingVML = strings.Replace(sheetRelationshipsDrawingVML, "..", "xl", -1)
|
||||||
|
} else {
|
||||||
|
// Add first comment for given sheet.
|
||||||
|
rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingVML, sheetRelationshipsDrawingVML, "")
|
||||||
|
f.addSheetRelationships(sheet, SourceRelationshipComments, sheetRelationshipsComments, "")
|
||||||
|
f.addSheetLegacyDrawing(sheet, rID)
|
||||||
|
}
|
||||||
|
commentsXML := "xl/comments" + strconv.Itoa(commentID) + ".xml"
|
||||||
|
f.addComment(commentsXML, cell, formatSet)
|
||||||
|
var colCount int
|
||||||
|
for i, l := range strings.Split(formatSet.Text, "\n") {
|
||||||
|
if ll := len(l); ll > colCount {
|
||||||
|
if i == 0 {
|
||||||
|
ll += len(formatSet.Author)
|
||||||
|
}
|
||||||
|
colCount = ll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.addDrawingVML(commentID, drawingVML, cell, strings.Count(formatSet.Text, "\n")+1, colCount)
|
||||||
|
f.addContentTypePart(commentID, "comments")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDrawingVML provides a function to create comment as
|
||||||
|
// xl/drawings/vmlDrawing%d.vml by given commit ID and cell.
|
||||||
|
func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount, colCount int) {
|
||||||
|
col := string(strings.Map(letterOnlyMapF, cell))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
|
||||||
|
xAxis := row - 1
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
vml := vmlDrawing{
|
||||||
|
XMLNSv: "urn:schemas-microsoft-com:vml",
|
||||||
|
XMLNSo: "urn:schemas-microsoft-com:office:office",
|
||||||
|
XMLNSx: "urn:schemas-microsoft-com:office:excel",
|
||||||
|
XMLNSmv: "http://macVmlSchemaUri",
|
||||||
|
Shapelayout: &xlsxShapelayout{
|
||||||
|
Ext: "edit",
|
||||||
|
IDmap: &xlsxIDmap{
|
||||||
|
Ext: "edit",
|
||||||
|
Data: commentID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Shapetype: &xlsxShapetype{
|
||||||
|
ID: "_x0000_t202",
|
||||||
|
Coordsize: "21600,21600",
|
||||||
|
Spt: 202,
|
||||||
|
Path: "m0,0l0,21600,21600,21600,21600,0xe",
|
||||||
|
Stroke: &xlsxStroke{
|
||||||
|
Joinstyle: "miter",
|
||||||
|
},
|
||||||
|
VPath: &vPath{
|
||||||
|
Gradientshapeok: "t",
|
||||||
|
Connecttype: "miter",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
sp := encodeShape{
|
||||||
|
Fill: &vFill{
|
||||||
|
Color2: "#fbfe82",
|
||||||
|
Angle: -180,
|
||||||
|
Type: "gradient",
|
||||||
|
Fill: &oFill{
|
||||||
|
Ext: "view",
|
||||||
|
Type: "gradientUnscaled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Shadow: &vShadow{
|
||||||
|
On: "t",
|
||||||
|
Color: "black",
|
||||||
|
Obscured: "t",
|
||||||
|
},
|
||||||
|
Path: &vPath{
|
||||||
|
Connecttype: "none",
|
||||||
|
},
|
||||||
|
Textbox: &vTextbox{
|
||||||
|
Style: "mso-direction-alt:auto",
|
||||||
|
Div: &xlsxDiv{
|
||||||
|
Style: "text-align:left",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ClientData: &xClientData{
|
||||||
|
ObjectType: "Note",
|
||||||
|
Anchor: fmt.Sprintf(
|
||||||
|
"%d, 23, %d, 0, %d, %d, %d, 5",
|
||||||
|
1+yAxis, 1+xAxis, 2+yAxis+lineCount, colCount+yAxis, 2+xAxis+lineCount),
|
||||||
|
AutoFill: "True",
|
||||||
|
Row: xAxis,
|
||||||
|
Column: yAxis,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s, _ := xml.Marshal(sp)
|
||||||
|
shape := xlsxShape{
|
||||||
|
ID: "_x0000_s1025",
|
||||||
|
Type: "#_x0000_t202",
|
||||||
|
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
|
||||||
|
Fillcolor: "#fbf6d6",
|
||||||
|
Strokecolor: "#edeaa1",
|
||||||
|
Val: string(s[13 : len(s)-14]),
|
||||||
|
}
|
||||||
|
c, ok := f.XLSX[drawingVML]
|
||||||
|
if ok {
|
||||||
|
d := decodeVmlDrawing{}
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
|
||||||
|
for _, v := range d.Shape {
|
||||||
|
s := xlsxShape{
|
||||||
|
ID: "_x0000_s1025",
|
||||||
|
Type: "#_x0000_t202",
|
||||||
|
Style: "position:absolute;73.5pt;width:108pt;height:59.25pt;z-index:1;visibility:hidden",
|
||||||
|
Fillcolor: "#fbf6d6",
|
||||||
|
Strokecolor: "#edeaa1",
|
||||||
|
Val: v.Val,
|
||||||
|
}
|
||||||
|
vml.Shape = append(vml.Shape, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vml.Shape = append(vml.Shape, shape)
|
||||||
|
v, _ := xml.Marshal(vml)
|
||||||
|
f.XLSX[drawingVML] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// addComment provides a function to create chart as xl/comments%d.xml by
|
||||||
|
// given cell and format sets.
|
||||||
|
func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
|
||||||
|
a := formatSet.Author
|
||||||
|
t := formatSet.Text
|
||||||
|
if len(a) > 255 {
|
||||||
|
a = a[0:255]
|
||||||
|
}
|
||||||
|
if len(t) > 32512 {
|
||||||
|
t = t[0:32512]
|
||||||
|
}
|
||||||
|
comments := xlsxComments{
|
||||||
|
Authors: []xlsxAuthor{
|
||||||
|
{
|
||||||
|
Author: formatSet.Author,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmt := xlsxComment{
|
||||||
|
Ref: cell,
|
||||||
|
AuthorID: 0,
|
||||||
|
Text: xlsxText{
|
||||||
|
R: []xlsxR{
|
||||||
|
{
|
||||||
|
RPr: &xlsxRPr{
|
||||||
|
B: " ",
|
||||||
|
Sz: &attrValFloat{Val: 9},
|
||||||
|
Color: &xlsxColor{
|
||||||
|
Indexed: 81,
|
||||||
|
},
|
||||||
|
RFont: &attrValString{Val: "Calibri"},
|
||||||
|
Family: &attrValInt{Val: 2},
|
||||||
|
},
|
||||||
|
T: a,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RPr: &xlsxRPr{
|
||||||
|
Sz: &attrValFloat{Val: 9},
|
||||||
|
Color: &xlsxColor{
|
||||||
|
Indexed: 81,
|
||||||
|
},
|
||||||
|
RFont: &attrValString{Val: "Calibri"},
|
||||||
|
Family: &attrValInt{Val: 2},
|
||||||
|
},
|
||||||
|
T: t,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c, ok := f.XLSX[commentsXML]
|
||||||
|
if ok {
|
||||||
|
d := xlsxComments{}
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
|
||||||
|
comments.CommentList.Comment = append(comments.CommentList.Comment, d.CommentList.Comment...)
|
||||||
|
}
|
||||||
|
comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
|
||||||
|
v, _ := xml.Marshal(comments)
|
||||||
|
f.saveFileList(commentsXML, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// countComments provides a function to get comments files count storage in
|
||||||
|
// the folder xl.
|
||||||
|
func (f *File) countComments() int {
|
||||||
|
count := 0
|
||||||
|
for k := range f.XLSX {
|
||||||
|
if strings.Contains(k, "xl/comments") {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
233
vendor/github.com/360EntSecGroup-Skylar/excelize/datavalidation.go
generated
vendored
Normal file
233
vendor/github.com/360EntSecGroup-Skylar/excelize/datavalidation.go
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataValidationType defined the type of data validation.
|
||||||
|
type DataValidationType int
|
||||||
|
|
||||||
|
// Data validation types.
|
||||||
|
const (
|
||||||
|
_DataValidationType = iota
|
||||||
|
typeNone // inline use
|
||||||
|
DataValidationTypeCustom
|
||||||
|
DataValidationTypeDate
|
||||||
|
DataValidationTypeDecimal
|
||||||
|
typeList // inline use
|
||||||
|
DataValidationTypeTextLeng
|
||||||
|
DataValidationTypeTime
|
||||||
|
// DataValidationTypeWhole Integer
|
||||||
|
DataValidationTypeWhole
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// dataValidationFormulaStrLen 255 characters+ 2 quotes
|
||||||
|
dataValidationFormulaStrLen = 257
|
||||||
|
// dataValidationFormulaStrLenErr
|
||||||
|
dataValidationFormulaStrLenErr = "data validation must be 0-255 characters"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataValidationErrorStyle defined the style of data validation error alert.
|
||||||
|
type DataValidationErrorStyle int
|
||||||
|
|
||||||
|
// Data validation error styles.
|
||||||
|
const (
|
||||||
|
_ DataValidationErrorStyle = iota
|
||||||
|
DataValidationErrorStyleStop
|
||||||
|
DataValidationErrorStyleWarning
|
||||||
|
DataValidationErrorStyleInformation
|
||||||
|
)
|
||||||
|
|
||||||
|
// Data validation error styles.
|
||||||
|
const (
|
||||||
|
styleStop = "stop"
|
||||||
|
styleWarning = "warning"
|
||||||
|
styleInformation = "information"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataValidationOperator operator enum.
|
||||||
|
type DataValidationOperator int
|
||||||
|
|
||||||
|
// Data validation operators.
|
||||||
|
const (
|
||||||
|
_DataValidationOperator = iota
|
||||||
|
DataValidationOperatorBetween
|
||||||
|
DataValidationOperatorEqual
|
||||||
|
DataValidationOperatorGreaterThan
|
||||||
|
DataValidationOperatorGreaterThanOrEqual
|
||||||
|
DataValidationOperatorLessThan
|
||||||
|
DataValidationOperatorLessThanOrEqual
|
||||||
|
DataValidationOperatorNotBetween
|
||||||
|
DataValidationOperatorNotEqual
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDataValidation return data validation struct.
|
||||||
|
func NewDataValidation(allowBlank bool) *DataValidation {
|
||||||
|
return &DataValidation{
|
||||||
|
AllowBlank: allowBlank,
|
||||||
|
ShowErrorMessage: false,
|
||||||
|
ShowInputMessage: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetError set error notice.
|
||||||
|
func (dd *DataValidation) SetError(style DataValidationErrorStyle, title, msg string) {
|
||||||
|
dd.Error = &msg
|
||||||
|
dd.ErrorTitle = &title
|
||||||
|
strStyle := styleStop
|
||||||
|
switch style {
|
||||||
|
case DataValidationErrorStyleStop:
|
||||||
|
strStyle = styleStop
|
||||||
|
case DataValidationErrorStyleWarning:
|
||||||
|
strStyle = styleWarning
|
||||||
|
case DataValidationErrorStyleInformation:
|
||||||
|
strStyle = styleInformation
|
||||||
|
|
||||||
|
}
|
||||||
|
dd.ShowErrorMessage = true
|
||||||
|
dd.ErrorStyle = &strStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInput set prompt notice.
|
||||||
|
func (dd *DataValidation) SetInput(title, msg string) {
|
||||||
|
dd.ShowInputMessage = true
|
||||||
|
dd.PromptTitle = &title
|
||||||
|
dd.Prompt = &msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDropList data validation list.
|
||||||
|
func (dd *DataValidation) SetDropList(keys []string) error {
|
||||||
|
dd.Formula1 = "\"" + strings.Join(keys, ",") + "\""
|
||||||
|
dd.Type = convDataValidationType(typeList)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRange provides function to set data validation range in drop list.
|
||||||
|
func (dd *DataValidation) SetRange(f1, f2 int, t DataValidationType, o DataValidationOperator) error {
|
||||||
|
formula1 := fmt.Sprintf("%d", f1)
|
||||||
|
formula2 := fmt.Sprintf("%d", f2)
|
||||||
|
if dataValidationFormulaStrLen < len(dd.Formula1) || dataValidationFormulaStrLen < len(dd.Formula2) {
|
||||||
|
return fmt.Errorf(dataValidationFormulaStrLenErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.Formula1 = formula1
|
||||||
|
dd.Formula2 = formula2
|
||||||
|
dd.Type = convDataValidationType(t)
|
||||||
|
dd.Operator = convDataValidationOperatior(o)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSqrefDropList provides set data validation on a range with source
|
||||||
|
// reference range of the worksheet by given data validation object and
|
||||||
|
// worksheet name. The data validation object can be created by
|
||||||
|
// NewDataValidation function. For example, set data validation on
|
||||||
|
// Sheet1!A7:B8 with validation criteria source Sheet1!E1:E3 settings, create
|
||||||
|
// in-cell dropdown by allowing list source:
|
||||||
|
//
|
||||||
|
// dvRange := excelize.NewDataValidation(true)
|
||||||
|
// dvRange.Sqref = "A7:B8"
|
||||||
|
// dvRange.SetSqrefDropList("E1:E3", true)
|
||||||
|
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||||
|
//
|
||||||
|
func (dd *DataValidation) SetSqrefDropList(sqref string, isCurrentSheet bool) error {
|
||||||
|
if isCurrentSheet {
|
||||||
|
dd.Formula1 = sqref
|
||||||
|
dd.Type = convDataValidationType(typeList)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cross-sheet sqref cell are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSqref provides function to set data validation range in drop list.
|
||||||
|
func (dd *DataValidation) SetSqref(sqref string) {
|
||||||
|
if dd.Sqref == "" {
|
||||||
|
dd.Sqref = sqref
|
||||||
|
} else {
|
||||||
|
dd.Sqref = fmt.Sprintf("%s %s", dd.Sqref, sqref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convDataValidationType get excel data validation type.
|
||||||
|
func convDataValidationType(t DataValidationType) string {
|
||||||
|
typeMap := map[DataValidationType]string{
|
||||||
|
typeNone: "none",
|
||||||
|
DataValidationTypeCustom: "custom",
|
||||||
|
DataValidationTypeDate: "date",
|
||||||
|
DataValidationTypeDecimal: "decimal",
|
||||||
|
typeList: "list",
|
||||||
|
DataValidationTypeTextLeng: "textLength",
|
||||||
|
DataValidationTypeTime: "time",
|
||||||
|
DataValidationTypeWhole: "whole",
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeMap[t]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// convDataValidationOperatior get excel data validation operator.
|
||||||
|
func convDataValidationOperatior(o DataValidationOperator) string {
|
||||||
|
typeMap := map[DataValidationOperator]string{
|
||||||
|
DataValidationOperatorBetween: "between",
|
||||||
|
DataValidationOperatorEqual: "equal",
|
||||||
|
DataValidationOperatorGreaterThan: "greaterThan",
|
||||||
|
DataValidationOperatorGreaterThanOrEqual: "greaterThanOrEqual",
|
||||||
|
DataValidationOperatorLessThan: "lessThan",
|
||||||
|
DataValidationOperatorLessThanOrEqual: "lessThanOrEqual",
|
||||||
|
DataValidationOperatorNotBetween: "notBetween",
|
||||||
|
DataValidationOperatorNotEqual: "notEqual",
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeMap[o]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDataValidation provides set data validation on a range of the worksheet
|
||||||
|
// by given data validation object and worksheet name. The data validation
|
||||||
|
// object can be created by NewDataValidation function.
|
||||||
|
//
|
||||||
|
// Example 1, set data validation on Sheet1!A1:B2 with validation criteria
|
||||||
|
// settings, show error alert after invalid data is entered with "Stop" style
|
||||||
|
// and custom title "error body":
|
||||||
|
//
|
||||||
|
// dvRange := excelize.NewDataValidation(true)
|
||||||
|
// dvRange.Sqref = "A1:B2"
|
||||||
|
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
|
||||||
|
// dvRange.SetError(excelize.DataValidationErrorStyleStop, "error title", "error body")
|
||||||
|
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||||
|
//
|
||||||
|
// Example 2, set data validation on Sheet1!A3:B4 with validation criteria
|
||||||
|
// settings, and show input message when cell is selected:
|
||||||
|
//
|
||||||
|
// dvRange = excelize.NewDataValidation(true)
|
||||||
|
// dvRange.Sqref = "A3:B4"
|
||||||
|
// dvRange.SetRange(10, 20, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorGreaterThan)
|
||||||
|
// dvRange.SetInput("input title", "input body")
|
||||||
|
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||||
|
//
|
||||||
|
// Example 3, set data validation on Sheet1!A5:B6 with validation criteria
|
||||||
|
// settings, create in-cell dropdown by allowing list source:
|
||||||
|
//
|
||||||
|
// dvRange = excelize.NewDataValidation(true)
|
||||||
|
// dvRange.Sqref = "A5:B6"
|
||||||
|
// dvRange.SetDropList([]string{"1", "2", "3"})
|
||||||
|
// xlsx.AddDataValidation("Sheet1", dvRange)
|
||||||
|
//
|
||||||
|
func (f *File) AddDataValidation(sheet string, dv *DataValidation) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if nil == xlsx.DataValidations {
|
||||||
|
xlsx.DataValidations = new(xlsxDataValidations)
|
||||||
|
}
|
||||||
|
xlsx.DataValidations.DataValidation = append(xlsx.DataValidations.DataValidation, dv)
|
||||||
|
xlsx.DataValidations.Count = len(xlsx.DataValidations.DataValidation)
|
||||||
|
}
|
||||||
151
vendor/github.com/360EntSecGroup-Skylar/excelize/date.go
generated
vendored
Normal file
151
vendor/github.com/360EntSecGroup-Skylar/excelize/date.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// timeLocationUTC defined the UTC time location.
|
||||||
|
var timeLocationUTC, _ = time.LoadLocation("UTC")
|
||||||
|
|
||||||
|
// timeToUTCTime provides a function to convert time to UTC time.
|
||||||
|
func timeToUTCTime(t time.Time) time.Time {
|
||||||
|
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), timeLocationUTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeToExcelTime provides a function to convert time to Excel time.
|
||||||
|
func timeToExcelTime(t time.Time) float64 {
|
||||||
|
// TODO in future this should probably also handle date1904 and like TimeFromExcelTime
|
||||||
|
var excelTime float64
|
||||||
|
var deltaDays int64
|
||||||
|
excelTime = 0
|
||||||
|
deltaDays = 290 * 364
|
||||||
|
// check if UnixNano would be out of int64 range
|
||||||
|
for t.Unix() > deltaDays*24*60*60 {
|
||||||
|
// reduce by aprox. 290 years, which is max for int64 nanoseconds
|
||||||
|
delta := time.Duration(deltaDays) * 24 * time.Hour
|
||||||
|
excelTime = excelTime + float64(deltaDays)
|
||||||
|
t = t.Add(-delta)
|
||||||
|
}
|
||||||
|
// finally add remainder of UnixNano to keep nano precision
|
||||||
|
// and 25569 which is days between 1900 and 1970
|
||||||
|
return excelTime + float64(t.UnixNano())/8.64e13 + 25569.0
|
||||||
|
}
|
||||||
|
|
||||||
|
// shiftJulianToNoon provides a function to process julian date to noon.
|
||||||
|
func shiftJulianToNoon(julianDays, julianFraction float64) (float64, float64) {
|
||||||
|
switch {
|
||||||
|
case -0.5 < julianFraction && julianFraction < 0.5:
|
||||||
|
julianFraction += 0.5
|
||||||
|
case julianFraction >= 0.5:
|
||||||
|
julianDays++
|
||||||
|
julianFraction -= 0.5
|
||||||
|
case julianFraction <= -0.5:
|
||||||
|
julianDays--
|
||||||
|
julianFraction += 1.5
|
||||||
|
}
|
||||||
|
return julianDays, julianFraction
|
||||||
|
}
|
||||||
|
|
||||||
|
// fractionOfADay provides a function to return the integer values for hour,
|
||||||
|
// minutes, seconds and nanoseconds that comprised a given fraction of a day.
|
||||||
|
// values would round to 1 us.
|
||||||
|
func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
|
||||||
|
|
||||||
|
const (
|
||||||
|
c1us = 1e3
|
||||||
|
c1s = 1e9
|
||||||
|
c1day = 24 * 60 * 60 * c1s
|
||||||
|
)
|
||||||
|
|
||||||
|
frac := int64(c1day*fraction + c1us/2)
|
||||||
|
nanoseconds = int((frac%c1s)/c1us) * c1us
|
||||||
|
frac /= c1s
|
||||||
|
seconds = int(frac % 60)
|
||||||
|
frac /= 60
|
||||||
|
minutes = int(frac % 60)
|
||||||
|
hours = int(frac / 60)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// julianDateToGregorianTime provides a function to convert julian date to
|
||||||
|
// gregorian time.
|
||||||
|
func julianDateToGregorianTime(part1, part2 float64) time.Time {
|
||||||
|
part1I, part1F := math.Modf(part1)
|
||||||
|
part2I, part2F := math.Modf(part2)
|
||||||
|
julianDays := part1I + part2I
|
||||||
|
julianFraction := part1F + part2F
|
||||||
|
julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
|
||||||
|
day, month, year := doTheFliegelAndVanFlandernAlgorithm(int(julianDays))
|
||||||
|
hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
|
||||||
|
return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// doTheFliegelAndVanFlandernAlgorithm; By this point generations of
|
||||||
|
// programmers have repeated the algorithm sent to the editor of
|
||||||
|
// "Communications of the ACM" in 1968 (published in CACM, volume 11, number
|
||||||
|
// 10, October 1968, p.657). None of those programmers seems to have found it
|
||||||
|
// necessary to explain the constants or variable names set out by Henry F.
|
||||||
|
// Fliegel and Thomas C. Van Flandern. Maybe one day I'll buy that jounal and
|
||||||
|
// expand an explanation here - that day is not today.
|
||||||
|
func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
|
||||||
|
l := jd + 68569
|
||||||
|
n := (4 * l) / 146097
|
||||||
|
l = l - (146097*n+3)/4
|
||||||
|
i := (4000 * (l + 1)) / 1461001
|
||||||
|
l = l - (1461*i)/4 + 31
|
||||||
|
j := (80 * l) / 2447
|
||||||
|
d := l - (2447*j)/80
|
||||||
|
l = j / 11
|
||||||
|
m := j + 2 - (12 * l)
|
||||||
|
y := 100*(n-49) + i + l
|
||||||
|
return d, m, y
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeFromExcelTime provides a function to convert an excelTime
|
||||||
|
// representation (stored as a floating point number) to a time.Time.
|
||||||
|
func timeFromExcelTime(excelTime float64, date1904 bool) time.Time {
|
||||||
|
const MDD int64 = 106750 // Max time.Duration Days, aprox. 290 years
|
||||||
|
var date time.Time
|
||||||
|
var intPart = int64(excelTime)
|
||||||
|
// Excel uses Julian dates prior to March 1st 1900, and Gregorian
|
||||||
|
// thereafter.
|
||||||
|
if intPart <= 61 {
|
||||||
|
const OFFSET1900 = 15018.0
|
||||||
|
const OFFSET1904 = 16480.0
|
||||||
|
const MJD0 float64 = 2400000.5
|
||||||
|
var date time.Time
|
||||||
|
if date1904 {
|
||||||
|
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1904)
|
||||||
|
} else {
|
||||||
|
date = julianDateToGregorianTime(MJD0, excelTime+OFFSET1900)
|
||||||
|
}
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
var floatPart = excelTime - float64(intPart)
|
||||||
|
var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
|
||||||
|
if date1904 {
|
||||||
|
date = time.Date(1904, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
} else {
|
||||||
|
date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duration is limited to aprox. 290 years
|
||||||
|
for intPart > MDD {
|
||||||
|
durationDays := time.Duration(MDD) * time.Hour * 24
|
||||||
|
date = date.Add(durationDays)
|
||||||
|
intPart = intPart - MDD
|
||||||
|
}
|
||||||
|
durationDays := time.Duration(intPart) * time.Hour * 24
|
||||||
|
durationPart := time.Duration(dayNanoSeconds * floatPart)
|
||||||
|
return date.Add(durationDays).Add(durationPart)
|
||||||
|
}
|
||||||
456
vendor/github.com/360EntSecGroup-Skylar/excelize/excelize.go
generated
vendored
Normal file
456
vendor/github.com/360EntSecGroup-Skylar/excelize/excelize.go
generated
vendored
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
//
|
||||||
|
// See https://xuri.me/excelize for more information about this package.
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File define a populated XLSX file struct.
|
||||||
|
type File struct {
|
||||||
|
checked map[string]bool
|
||||||
|
sheetMap map[string]string
|
||||||
|
ContentTypes *xlsxTypes
|
||||||
|
Path string
|
||||||
|
SharedStrings *xlsxSST
|
||||||
|
Sheet map[string]*xlsxWorksheet
|
||||||
|
SheetCount int
|
||||||
|
Styles *xlsxStyleSheet
|
||||||
|
Theme *xlsxTheme
|
||||||
|
WorkBook *xlsxWorkbook
|
||||||
|
WorkBookRels *xlsxWorkbookRels
|
||||||
|
XLSX map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile take the name of an XLSX file and returns a populated XLSX file
|
||||||
|
// struct for it.
|
||||||
|
func OpenFile(filename string) (*File, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
f, err := OpenReader(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.Path = filename
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenReader take an io.Reader and return a populated XLSX file.
|
||||||
|
func OpenReader(r io.Reader) (*File, error) {
|
||||||
|
b, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
file, sheetCount, err := ReadZipReader(zr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := &File{
|
||||||
|
checked: make(map[string]bool),
|
||||||
|
Sheet: make(map[string]*xlsxWorksheet),
|
||||||
|
SheetCount: sheetCount,
|
||||||
|
XLSX: file,
|
||||||
|
}
|
||||||
|
f.sheetMap = f.getSheetMap()
|
||||||
|
f.Styles = f.stylesReader()
|
||||||
|
f.Theme = f.themeReader()
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaultTimeStyle provides a function to set default numbers format for
|
||||||
|
// time.Time type cell value by given worksheet name, cell coordinates and
|
||||||
|
// number format code.
|
||||||
|
func (f *File) setDefaultTimeStyle(sheet, axis string, format int) {
|
||||||
|
if f.GetCellStyle(sheet, axis) == 0 {
|
||||||
|
style, _ := f.NewStyle(`{"number_format": ` + strconv.Itoa(format) + `}`)
|
||||||
|
f.SetCellStyle(sheet, axis, axis, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// workSheetReader provides a function to get the pointer to the structure
|
||||||
|
// after deserialization by given worksheet name.
|
||||||
|
func (f *File) workSheetReader(sheet string) *xlsxWorksheet {
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
name = "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
|
||||||
|
}
|
||||||
|
if f.Sheet[name] == nil {
|
||||||
|
var xlsx xlsxWorksheet
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(name)), &xlsx)
|
||||||
|
if f.checked == nil {
|
||||||
|
f.checked = make(map[string]bool)
|
||||||
|
}
|
||||||
|
ok := f.checked[name]
|
||||||
|
if !ok {
|
||||||
|
checkSheet(&xlsx)
|
||||||
|
checkRow(&xlsx)
|
||||||
|
f.checked[name] = true
|
||||||
|
}
|
||||||
|
f.Sheet[name] = &xlsx
|
||||||
|
}
|
||||||
|
return f.Sheet[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkSheet provides a function to fill each row element and make that is
|
||||||
|
// continuous in a worksheet of XML.
|
||||||
|
func checkSheet(xlsx *xlsxWorksheet) {
|
||||||
|
row := len(xlsx.SheetData.Row)
|
||||||
|
if row >= 1 {
|
||||||
|
lastRow := xlsx.SheetData.Row[row-1].R
|
||||||
|
if lastRow >= row {
|
||||||
|
row = lastRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sheetData := xlsxSheetData{}
|
||||||
|
existsRows := map[int]int{}
|
||||||
|
for k := range xlsx.SheetData.Row {
|
||||||
|
existsRows[xlsx.SheetData.Row[k].R] = k
|
||||||
|
}
|
||||||
|
for i := 0; i < row; i++ {
|
||||||
|
_, ok := existsRows[i+1]
|
||||||
|
if ok {
|
||||||
|
sheetData.Row = append(sheetData.Row, xlsx.SheetData.Row[existsRows[i+1]])
|
||||||
|
} else {
|
||||||
|
sheetData.Row = append(sheetData.Row, xlsxRow{
|
||||||
|
R: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SheetData = sheetData
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceWorkSheetsRelationshipsNameSpaceBytes provides a function to replace
|
||||||
|
// xl/worksheets/sheet%d.xml XML tags to self-closing for compatible Microsoft
|
||||||
|
// Office Excel 2007.
|
||||||
|
func replaceWorkSheetsRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
|
||||||
|
var oldXmlns = []byte(`<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||||
|
var newXmlns = []byte(`<worksheet xr:uid="{00000000-0001-0000-0000-000000000000}" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" mc:Ignorable="x14ac xr xr2 xr3" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mx="http://schemas.microsoft.com/office/mac/excel/2008/main" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||||
|
workbookMarshal = bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
|
||||||
|
return workbookMarshal
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLinkedValue fix linked values within a spreadsheet are not updating in
|
||||||
|
// Office Excel 2007 and 2010. This function will be remove value tag when met a
|
||||||
|
// cell have a linked value. Reference
|
||||||
|
// https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating?forum=excel
|
||||||
|
//
|
||||||
|
// Notice: after open XLSX file Excel will be update linked value and generate
|
||||||
|
// new value and will prompt save file or not.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// <row r="19" spans="2:2">
|
||||||
|
// <c r="B19">
|
||||||
|
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
|
||||||
|
// <v>100</v>
|
||||||
|
// </c>
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
// to
|
||||||
|
//
|
||||||
|
// <row r="19" spans="2:2">
|
||||||
|
// <c r="B19">
|
||||||
|
// <f>SUM(Sheet2!D2,Sheet2!D11)</f>
|
||||||
|
// </c>
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
func (f *File) UpdateLinkedValue() {
|
||||||
|
for _, name := range f.GetSheetMap() {
|
||||||
|
xlsx := f.workSheetReader(name)
|
||||||
|
for indexR := range xlsx.SheetData.Row {
|
||||||
|
for indexC, col := range xlsx.SheetData.Row[indexR].C {
|
||||||
|
if col.F != nil && col.V != "" {
|
||||||
|
xlsx.SheetData.Row[indexR].C[indexC].V = ""
|
||||||
|
xlsx.SheetData.Row[indexR].C[indexC].T = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustHelper provides a function to adjust rows and columns dimensions,
|
||||||
|
// hyperlinks, merged cells and auto filter when inserting or deleting rows or
|
||||||
|
// columns.
|
||||||
|
//
|
||||||
|
// sheet: Worksheet name that we're editing
|
||||||
|
// column: Index number of the column we're inserting/deleting before
|
||||||
|
// row: Index number of the row we're inserting/deleting before
|
||||||
|
// offset: Number of rows/column to insert/delete negative values indicate deletion
|
||||||
|
//
|
||||||
|
// TODO: adjustPageBreaks, adjustComments, adjustDataValidations, adjustProtectedCells
|
||||||
|
//
|
||||||
|
func (f *File) adjustHelper(sheet string, column, row, offset int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
f.adjustRowDimensions(xlsx, row, offset)
|
||||||
|
f.adjustColDimensions(xlsx, column, offset)
|
||||||
|
f.adjustHyperlinks(sheet, column, row, offset)
|
||||||
|
f.adjustMergeCells(xlsx, column, row, offset)
|
||||||
|
f.adjustAutoFilter(xlsx, column, row, offset)
|
||||||
|
checkSheet(xlsx)
|
||||||
|
checkRow(xlsx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustColDimensions provides a function to update column dimensions when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustColDimensions(xlsx *xlsxWorksheet, column, offset int) {
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
for k, v := range r.C {
|
||||||
|
axis := v.R
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if yAxis >= column && column != -1 {
|
||||||
|
xlsx.SheetData.Row[i].C[k].R = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustRowDimensions provides a function to update row dimensions when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustRowDimensions(xlsx *xlsxWorksheet, rowIndex, offset int) {
|
||||||
|
if rowIndex == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if r.R >= rowIndex {
|
||||||
|
f.ajustSingleRowDimensions(&xlsx.SheetData.Row[i], offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ajustSingleRowDimensions provides a function to ajust single row
|
||||||
|
// dimensions.
|
||||||
|
func (f *File) ajustSingleRowDimensions(r *xlsxRow, offset int) {
|
||||||
|
r.R += offset
|
||||||
|
for i, col := range r.C {
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, col.R))
|
||||||
|
r.C[i].R = string(strings.Map(letterOnlyMapF, col.R)) + strconv.Itoa(row+offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustHyperlinks provides a function to update hyperlinks when inserting or
|
||||||
|
// deleting rows or columns.
|
||||||
|
func (f *File) adjustHyperlinks(sheet string, column, rowIndex, offset int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
|
||||||
|
// order is important
|
||||||
|
if xlsx.Hyperlinks != nil && offset < 0 {
|
||||||
|
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||||
|
axis := v.Ref
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if row == rowIndex || yAxis == column {
|
||||||
|
f.deleteSheetRelationships(sheet, v.RID)
|
||||||
|
if len(xlsx.Hyperlinks.Hyperlink) > 1 {
|
||||||
|
xlsx.Hyperlinks.Hyperlink = append(xlsx.Hyperlinks.Hyperlink[:i], xlsx.Hyperlinks.Hyperlink[i+1:]...)
|
||||||
|
} else {
|
||||||
|
xlsx.Hyperlinks = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if xlsx.Hyperlinks != nil {
|
||||||
|
for i, v := range xlsx.Hyperlinks.Hyperlink {
|
||||||
|
axis := v.Ref
|
||||||
|
col := string(strings.Map(letterOnlyMapF, axis))
|
||||||
|
row, _ := strconv.Atoi(strings.Map(intOnlyMapF, axis))
|
||||||
|
xAxis := row + offset
|
||||||
|
yAxis := TitleToNumber(col)
|
||||||
|
if rowIndex != -1 && row >= rowIndex {
|
||||||
|
xlsx.Hyperlinks.Hyperlink[i].Ref = col + strconv.Itoa(xAxis)
|
||||||
|
}
|
||||||
|
if column != -1 && yAxis >= column {
|
||||||
|
xlsx.Hyperlinks.Hyperlink[i].Ref = ToAlphaString(yAxis+offset) + strconv.Itoa(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustMergeCellsHelper provides a function to update merged cells when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustMergeCellsHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
if xlsx.MergeCells != nil {
|
||||||
|
for k, v := range xlsx.MergeCells.Cells {
|
||||||
|
beg := strings.Split(v.Ref, ":")[0]
|
||||||
|
end := strings.Split(v.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begxAxis := begrow + offset
|
||||||
|
begyAxis := TitleToNumber(begcol)
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
endxAxis := endrow + offset
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
|
||||||
|
if rowIndex != -1 {
|
||||||
|
if begrow > 1 && begrow >= rowIndex {
|
||||||
|
beg = begcol + strconv.Itoa(begxAxis)
|
||||||
|
}
|
||||||
|
if endrow > 1 && endrow >= rowIndex {
|
||||||
|
end = endcol + strconv.Itoa(endxAxis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if column != -1 {
|
||||||
|
if begyAxis >= column {
|
||||||
|
beg = ToAlphaString(begyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
if endyAxis >= column {
|
||||||
|
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xlsx.MergeCells.Cells[k].Ref = beg + ":" + end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustMergeCells provides a function to update merged cells when inserting
|
||||||
|
// or deleting rows or columns.
|
||||||
|
func (f *File) adjustMergeCells(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
f.adjustMergeCellsHelper(xlsx, column, rowIndex, offset)
|
||||||
|
|
||||||
|
if xlsx.MergeCells != nil && offset < 0 {
|
||||||
|
for k, v := range xlsx.MergeCells.Cells {
|
||||||
|
beg := strings.Split(v.Ref, ":")[0]
|
||||||
|
end := strings.Split(v.Ref, ":")[1]
|
||||||
|
if beg == end {
|
||||||
|
xlsx.MergeCells.Count += offset
|
||||||
|
if len(xlsx.MergeCells.Cells) > 1 {
|
||||||
|
xlsx.MergeCells.Cells = append(xlsx.MergeCells.Cells[:k], xlsx.MergeCells.Cells[k+1:]...)
|
||||||
|
} else {
|
||||||
|
xlsx.MergeCells = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustAutoFilter provides a function to update the auto filter when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustAutoFilter(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
f.adjustAutoFilterHelper(xlsx, column, rowIndex, offset)
|
||||||
|
|
||||||
|
if xlsx.AutoFilter != nil {
|
||||||
|
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||||
|
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begxAxis := begrow + offset
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
endxAxis := endrow + offset
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
|
||||||
|
if rowIndex != -1 {
|
||||||
|
if begrow >= rowIndex {
|
||||||
|
beg = begcol + strconv.Itoa(begxAxis)
|
||||||
|
}
|
||||||
|
if endrow >= rowIndex {
|
||||||
|
end = endcol + strconv.Itoa(endxAxis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if column != -1 && endyAxis >= column {
|
||||||
|
end = ToAlphaString(endyAxis+offset) + strconv.Itoa(endrow)
|
||||||
|
}
|
||||||
|
xlsx.AutoFilter.Ref = beg + ":" + end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjustAutoFilterHelper provides a function to update the auto filter when
|
||||||
|
// inserting or deleting rows or columns.
|
||||||
|
func (f *File) adjustAutoFilterHelper(xlsx *xlsxWorksheet, column, rowIndex, offset int) {
|
||||||
|
if xlsx.AutoFilter != nil {
|
||||||
|
beg := strings.Split(xlsx.AutoFilter.Ref, ":")[0]
|
||||||
|
end := strings.Split(xlsx.AutoFilter.Ref, ":")[1]
|
||||||
|
|
||||||
|
begcol := string(strings.Map(letterOnlyMapF, beg))
|
||||||
|
begrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, beg))
|
||||||
|
begyAxis := TitleToNumber(begcol)
|
||||||
|
|
||||||
|
endcol := string(strings.Map(letterOnlyMapF, end))
|
||||||
|
endyAxis := TitleToNumber(endcol)
|
||||||
|
endrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, end))
|
||||||
|
|
||||||
|
if (begrow == rowIndex && offset < 0) || (column == begyAxis && column == endyAxis) {
|
||||||
|
xlsx.AutoFilter = nil
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if begrow < r.R && r.R <= endrow {
|
||||||
|
xlsx.SheetData.Row[i].Hidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMergeCells provides a function to get all merged cells from a worksheet currently.
|
||||||
|
func (f *File) GetMergeCells(sheet string) []MergeCell {
|
||||||
|
mergeCells := []MergeCell{}
|
||||||
|
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.MergeCells != nil {
|
||||||
|
for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
|
||||||
|
ref := xlsx.MergeCells.Cells[i].Ref
|
||||||
|
axis := strings.Split(ref, ":")[0]
|
||||||
|
mergeCells = append(mergeCells, []string{ref, f.GetCellValue(sheet, axis)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergeCells
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeCell define a merged cell data.
|
||||||
|
// It consists of the following structure.
|
||||||
|
// example: []string{"D4:E10", "cell value"}
|
||||||
|
type MergeCell []string
|
||||||
|
|
||||||
|
// GetCellValue returns merged cell value.
|
||||||
|
func (m *MergeCell) GetCellValue() string {
|
||||||
|
return (*m)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStartAxis returns the merge start axis.
|
||||||
|
// example: "C2"
|
||||||
|
func (m *MergeCell) GetStartAxis() string {
|
||||||
|
axis := strings.Split((*m)[0], ":")
|
||||||
|
return axis[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndAxis returns the merge end axis.
|
||||||
|
// example: "D4"
|
||||||
|
func (m *MergeCell) GetEndAxis() string {
|
||||||
|
axis := strings.Split((*m)[0], ":")
|
||||||
|
return axis[1]
|
||||||
|
}
|
||||||
BIN
vendor/github.com/360EntSecGroup-Skylar/excelize/excelize.png
generated
vendored
Normal file
BIN
vendor/github.com/360EntSecGroup-Skylar/excelize/excelize.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
106
vendor/github.com/360EntSecGroup-Skylar/excelize/file.go
generated
vendored
Normal file
106
vendor/github.com/360EntSecGroup-Skylar/excelize/file.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFile provides a function to create new file by default template. For
|
||||||
|
// example:
|
||||||
|
//
|
||||||
|
// xlsx := NewFile()
|
||||||
|
//
|
||||||
|
func NewFile() *File {
|
||||||
|
file := make(map[string][]byte)
|
||||||
|
file["_rels/.rels"] = []byte(XMLHeader + templateRels)
|
||||||
|
file["docProps/app.xml"] = []byte(XMLHeader + templateDocpropsApp)
|
||||||
|
file["docProps/core.xml"] = []byte(XMLHeader + templateDocpropsCore)
|
||||||
|
file["xl/_rels/workbook.xml.rels"] = []byte(XMLHeader + templateWorkbookRels)
|
||||||
|
file["xl/theme/theme1.xml"] = []byte(XMLHeader + templateTheme)
|
||||||
|
file["xl/worksheets/sheet1.xml"] = []byte(XMLHeader + templateSheet)
|
||||||
|
file["xl/styles.xml"] = []byte(XMLHeader + templateStyles)
|
||||||
|
file["xl/workbook.xml"] = []byte(XMLHeader + templateWorkbook)
|
||||||
|
file["[Content_Types].xml"] = []byte(XMLHeader + templateContentTypes)
|
||||||
|
f := &File{
|
||||||
|
sheetMap: make(map[string]string),
|
||||||
|
Sheet: make(map[string]*xlsxWorksheet),
|
||||||
|
SheetCount: 1,
|
||||||
|
XLSX: file,
|
||||||
|
}
|
||||||
|
f.ContentTypes = f.contentTypesReader()
|
||||||
|
f.Styles = f.stylesReader()
|
||||||
|
f.WorkBook = f.workbookReader()
|
||||||
|
f.WorkBookRels = f.workbookRelsReader()
|
||||||
|
f.Sheet["xl/worksheets/sheet1.xml"] = f.workSheetReader("Sheet1")
|
||||||
|
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
|
||||||
|
f.Theme = f.themeReader()
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save provides a function to override the xlsx file with origin path.
|
||||||
|
func (f *File) Save() error {
|
||||||
|
if f.Path == "" {
|
||||||
|
return fmt.Errorf("no path defined for file, consider File.WriteTo or File.Write")
|
||||||
|
}
|
||||||
|
return f.SaveAs(f.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAs provides a function to create or update to an xlsx file at the
|
||||||
|
// provided path.
|
||||||
|
func (f *File) SaveAs(name string) error {
|
||||||
|
file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return f.Write(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write provides a function to write to an io.Writer.
|
||||||
|
func (f *File) Write(w io.Writer) error {
|
||||||
|
_, err := f.WriteTo(w)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo implements io.WriterTo to write the file.
|
||||||
|
func (f *File) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
buf, err := f.WriteToBuffer()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return buf.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToBuffer provides a function to get bytes.Buffer from the saved file.
|
||||||
|
func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
zw := zip.NewWriter(buf)
|
||||||
|
f.contentTypesWriter()
|
||||||
|
f.workbookWriter()
|
||||||
|
f.workbookRelsWriter()
|
||||||
|
f.worksheetWriter()
|
||||||
|
f.styleSheetWriter()
|
||||||
|
for path, content := range f.XLSX {
|
||||||
|
fi, err := zw.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return buf, err
|
||||||
|
}
|
||||||
|
_, err = fi.Write(content)
|
||||||
|
if err != nil {
|
||||||
|
return buf, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf, zw.Close()
|
||||||
|
}
|
||||||
8
vendor/github.com/360EntSecGroup-Skylar/excelize/go.mod
generated
vendored
Normal file
8
vendor/github.com/360EntSecGroup-Skylar/excelize/go.mod
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/360EntSecGroup-Skylar/excelize
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb
|
||||||
|
)
|
||||||
8
vendor/github.com/360EntSecGroup-Skylar/excelize/go.sum
generated
vendored
Normal file
8
vendor/github.com/360EntSecGroup-Skylar/excelize/go.sum
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb h1:cRItZejS4Ok67vfCdrbGIaqk86wmtQNOjVD7jSyS2aw=
|
||||||
|
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
139
vendor/github.com/360EntSecGroup-Skylar/excelize/hsl.go
generated
vendored
Normal file
139
vendor/github.com/360EntSecGroup-Skylar/excelize/hsl.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HSLModel converts any color.Color to a HSL color.
|
||||||
|
var HSLModel = color.ModelFunc(hslModel)
|
||||||
|
|
||||||
|
// HSL represents a cylindrical coordinate of points in an RGB color model.
|
||||||
|
//
|
||||||
|
// Values are in the range 0 to 1.
|
||||||
|
type HSL struct {
|
||||||
|
H, S, L float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGBA returns the alpha-premultiplied red, green, blue and alpha values
|
||||||
|
// for the HSL.
|
||||||
|
func (c HSL) RGBA() (uint32, uint32, uint32, uint32) {
|
||||||
|
r, g, b := HSLToRGB(c.H, c.S, c.L)
|
||||||
|
return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
// hslModel converts a color.Color to HSL.
|
||||||
|
func hslModel(c color.Color) color.Color {
|
||||||
|
if _, ok := c.(HSL); ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
r, g, b, _ := c.RGBA()
|
||||||
|
h, s, l := RGBToHSL(uint8(r>>8), uint8(g>>8), uint8(b>>8))
|
||||||
|
return HSL{h, s, l}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RGBToHSL converts an RGB triple to a HSL triple.
|
||||||
|
func RGBToHSL(r, g, b uint8) (h, s, l float64) {
|
||||||
|
fR := float64(r) / 255
|
||||||
|
fG := float64(g) / 255
|
||||||
|
fB := float64(b) / 255
|
||||||
|
max := math.Max(math.Max(fR, fG), fB)
|
||||||
|
min := math.Min(math.Min(fR, fG), fB)
|
||||||
|
l = (max + min) / 2
|
||||||
|
if max == min {
|
||||||
|
// Achromatic.
|
||||||
|
h, s = 0, 0
|
||||||
|
} else {
|
||||||
|
// Chromatic.
|
||||||
|
d := max - min
|
||||||
|
if l > 0.5 {
|
||||||
|
s = d / (2.0 - max - min)
|
||||||
|
} else {
|
||||||
|
s = d / (max + min)
|
||||||
|
}
|
||||||
|
switch max {
|
||||||
|
case fR:
|
||||||
|
h = (fG - fB) / d
|
||||||
|
if fG < fB {
|
||||||
|
h += 6
|
||||||
|
}
|
||||||
|
case fG:
|
||||||
|
h = (fB-fR)/d + 2
|
||||||
|
case fB:
|
||||||
|
h = (fR-fG)/d + 4
|
||||||
|
}
|
||||||
|
h /= 6
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// HSLToRGB converts an HSL triple to a RGB triple.
|
||||||
|
func HSLToRGB(h, s, l float64) (r, g, b uint8) {
|
||||||
|
var fR, fG, fB float64
|
||||||
|
if s == 0 {
|
||||||
|
fR, fG, fB = l, l, l
|
||||||
|
} else {
|
||||||
|
var q float64
|
||||||
|
if l < 0.5 {
|
||||||
|
q = l * (1 + s)
|
||||||
|
} else {
|
||||||
|
q = l + s - s*l
|
||||||
|
}
|
||||||
|
p := 2*l - q
|
||||||
|
fR = hueToRGB(p, q, h+1.0/3)
|
||||||
|
fG = hueToRGB(p, q, h)
|
||||||
|
fB = hueToRGB(p, q, h-1.0/3)
|
||||||
|
}
|
||||||
|
r = uint8((fR * 255) + 0.5)
|
||||||
|
g = uint8((fG * 255) + 0.5)
|
||||||
|
b = uint8((fB * 255) + 0.5)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// hueToRGB is a helper function for HSLToRGB.
|
||||||
|
func hueToRGB(p, q, t float64) float64 {
|
||||||
|
if t < 0 {
|
||||||
|
t++
|
||||||
|
}
|
||||||
|
if t > 1 {
|
||||||
|
t--
|
||||||
|
}
|
||||||
|
if t < 1.0/6 {
|
||||||
|
return p + (q-p)*6*t
|
||||||
|
}
|
||||||
|
if t < 0.5 {
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
if t < 2.0/3 {
|
||||||
|
return p + (q-p)*(2.0/3-t)*6
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
229
vendor/github.com/360EntSecGroup-Skylar/excelize/lib.go
generated
vendored
Normal file
229
vendor/github.com/360EntSecGroup-Skylar/excelize/lib.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReadZipReader can be used to read an XLSX in memory without touching the
|
||||||
|
// filesystem.
|
||||||
|
func ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
||||||
|
fileList := make(map[string][]byte)
|
||||||
|
worksheets := 0
|
||||||
|
for _, v := range r.File {
|
||||||
|
fileList[v.Name] = readFile(v)
|
||||||
|
if len(v.Name) > 18 {
|
||||||
|
if v.Name[0:19] == "xl/worksheets/sheet" {
|
||||||
|
worksheets++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileList, worksheets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readXML provides a function to read XML content as string.
|
||||||
|
func (f *File) readXML(name string) []byte {
|
||||||
|
if content, ok := f.XLSX[name]; ok {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// saveFileList provides a function to update given file content in file list
|
||||||
|
// of XLSX.
|
||||||
|
func (f *File) saveFileList(name string, content []byte) {
|
||||||
|
newContent := make([]byte, 0, len(XMLHeader)+len(content))
|
||||||
|
newContent = append(newContent, []byte(XMLHeader)...)
|
||||||
|
newContent = append(newContent, content...)
|
||||||
|
f.XLSX[name] = newContent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file content as string in a archive file.
|
||||||
|
func readFile(file *zip.File) []byte {
|
||||||
|
rc, err := file.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
buff := bytes.NewBuffer(nil)
|
||||||
|
_, _ = io.Copy(buff, rc)
|
||||||
|
rc.Close()
|
||||||
|
return buff.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAlphaString provides a function to convert integer to Excel sheet column
|
||||||
|
// title. For example convert 36 to column title AK:
|
||||||
|
//
|
||||||
|
// excelize.ToAlphaString(36)
|
||||||
|
//
|
||||||
|
func ToAlphaString(value int) string {
|
||||||
|
if value < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var ans string
|
||||||
|
i := value + 1
|
||||||
|
for i > 0 {
|
||||||
|
ans = string((i-1)%26+65) + ans
|
||||||
|
i = (i - 1) / 26
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
// TitleToNumber provides a function to convert Excel sheet column title to
|
||||||
|
// int (this function doesn't do value check currently). For example convert
|
||||||
|
// AK and ak to column title 36:
|
||||||
|
//
|
||||||
|
// excelize.TitleToNumber("AK")
|
||||||
|
// excelize.TitleToNumber("ak")
|
||||||
|
//
|
||||||
|
func TitleToNumber(s string) int {
|
||||||
|
weight := 0.0
|
||||||
|
sum := 0
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
ch := int(s[i])
|
||||||
|
if int(s[i]) >= int('a') && int(s[i]) <= int('z') {
|
||||||
|
ch = int(s[i]) - 32
|
||||||
|
}
|
||||||
|
sum = sum + (ch-int('A')+1)*int(math.Pow(26, weight))
|
||||||
|
weight++
|
||||||
|
}
|
||||||
|
return sum - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// letterOnlyMapF is used in conjunction with strings.Map to return only the
|
||||||
|
// characters A-Z and a-z in a string.
|
||||||
|
func letterOnlyMapF(rune rune) rune {
|
||||||
|
switch {
|
||||||
|
case 'A' <= rune && rune <= 'Z':
|
||||||
|
return rune
|
||||||
|
case 'a' <= rune && rune <= 'z':
|
||||||
|
return rune - 32
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// intOnlyMapF is used in conjunction with strings.Map to return only the
|
||||||
|
// numeric portions of a string.
|
||||||
|
func intOnlyMapF(rune rune) rune {
|
||||||
|
if rune >= 48 && rune < 58 {
|
||||||
|
return rune
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// boolPtr returns a pointer to a bool with the given value.
|
||||||
|
func boolPtr(b bool) *bool { return &b }
|
||||||
|
|
||||||
|
// defaultTrue returns true if b is nil, or the pointed value.
|
||||||
|
func defaultTrue(b *bool) bool {
|
||||||
|
if b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return *b
|
||||||
|
}
|
||||||
|
|
||||||
|
// axisLowerOrEqualThan returns true if axis1 <= axis2 axis1/axis2 can be
|
||||||
|
// either a column or a row axis, e.g. "A", "AAE", "42", "1", etc.
|
||||||
|
//
|
||||||
|
// For instance, the following comparisons are all true:
|
||||||
|
//
|
||||||
|
// "A" <= "B"
|
||||||
|
// "A" <= "AA"
|
||||||
|
// "B" <= "AA"
|
||||||
|
// "BC" <= "ABCD" (in a XLSX sheet, the BC col comes before the ABCD col)
|
||||||
|
// "1" <= "2"
|
||||||
|
// "2" <= "11" (in a XLSX sheet, the row 2 comes before the row 11)
|
||||||
|
// and so on
|
||||||
|
func axisLowerOrEqualThan(axis1, axis2 string) bool {
|
||||||
|
if len(axis1) < len(axis2) {
|
||||||
|
return true
|
||||||
|
} else if len(axis1) > len(axis2) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return axis1 <= axis2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCellColRow returns the two parts of a cell identifier (its col and row)
|
||||||
|
// as strings
|
||||||
|
//
|
||||||
|
// For instance:
|
||||||
|
//
|
||||||
|
// "C220" => "C", "220"
|
||||||
|
// "aaef42" => "aaef", "42"
|
||||||
|
// "" => "", ""
|
||||||
|
func getCellColRow(cell string) (col, row string) {
|
||||||
|
for index, rune := range cell {
|
||||||
|
if unicode.IsDigit(rune) {
|
||||||
|
return cell[:index], cell[index:]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFormatSet provides a method to convert format string to []byte and
|
||||||
|
// handle empty string.
|
||||||
|
func parseFormatSet(formatSet string) []byte {
|
||||||
|
if formatSet != "" {
|
||||||
|
return []byte(formatSet)
|
||||||
|
}
|
||||||
|
return []byte("{}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// namespaceStrictToTransitional provides a method to convert Strict and
|
||||||
|
// Transitional namespaces.
|
||||||
|
func namespaceStrictToTransitional(content []byte) []byte {
|
||||||
|
var namespaceTranslationDic = map[string]string{
|
||||||
|
StrictSourceRelationship: SourceRelationship,
|
||||||
|
StrictSourceRelationshipChart: SourceRelationshipChart,
|
||||||
|
StrictSourceRelationshipComments: SourceRelationshipComments,
|
||||||
|
StrictSourceRelationshipImage: SourceRelationshipImage,
|
||||||
|
StrictNameSpaceSpreadSheet: NameSpaceSpreadSheet,
|
||||||
|
}
|
||||||
|
for s, n := range namespaceTranslationDic {
|
||||||
|
content = bytes.Replace(content, []byte(s), []byte(n), -1)
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
// genSheetPasswd provides a method to generate password for worksheet
|
||||||
|
// protection by given plaintext. When an Excel sheet is being protected with
|
||||||
|
// a password, a 16-bit (two byte) long hash is generated. To verify a
|
||||||
|
// password, it is compared to the hash. Obviously, if the input data volume
|
||||||
|
// is great, numerous passwords will match the same hash. Here is the
|
||||||
|
// algorithm to create the hash value:
|
||||||
|
//
|
||||||
|
// take the ASCII values of all characters shift left the first character 1 bit, the second 2 bits and so on (use only the lower 15 bits and rotate all higher bits, the highest bit of the 16-bit value is always 0 [signed short])
|
||||||
|
// XOR all these values
|
||||||
|
// XOR the count of characters
|
||||||
|
// XOR the constant 0xCE4B
|
||||||
|
func genSheetPasswd(plaintext string) string {
|
||||||
|
var password int64 = 0x0000
|
||||||
|
var charPos uint = 1
|
||||||
|
for _, v := range plaintext {
|
||||||
|
value := int64(v) << charPos
|
||||||
|
charPos++
|
||||||
|
rotatedBits := value >> 15 // rotated bits beyond bit 15
|
||||||
|
value &= 0x7fff // first 15 bits
|
||||||
|
password ^= (value | rotatedBits)
|
||||||
|
}
|
||||||
|
password ^= int64(len(plaintext))
|
||||||
|
password ^= 0xCE4B
|
||||||
|
return strings.ToUpper(strconv.FormatInt(password, 16))
|
||||||
|
}
|
||||||
BIN
vendor/github.com/360EntSecGroup-Skylar/excelize/logo.png
generated
vendored
Normal file
BIN
vendor/github.com/360EntSecGroup-Skylar/excelize/logo.png
generated
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
533
vendor/github.com/360EntSecGroup-Skylar/excelize/picture.go
generated
vendored
Normal file
533
vendor/github.com/360EntSecGroup-Skylar/excelize/picture.go
generated
vendored
Normal file
@ -0,0 +1,533 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"image"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseFormatPictureSet provides a function to parse the format settings of
|
||||||
|
// the picture with default value.
|
||||||
|
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
|
||||||
|
format := formatPicture{
|
||||||
|
FPrintsWithSheet: true,
|
||||||
|
FLocksWithSheet: false,
|
||||||
|
NoChangeAspect: false,
|
||||||
|
OffsetX: 0,
|
||||||
|
OffsetY: 0,
|
||||||
|
XScale: 1.0,
|
||||||
|
YScale: 1.0,
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(parseFormatSet(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPicture provides the method to add picture in a sheet by given picture
|
||||||
|
// format set (such as offset, scale, aspect ratio setting and print settings)
|
||||||
|
// and file path. For example:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
// _ "image/gif"
|
||||||
|
// _ "image/jpeg"
|
||||||
|
// _ "image/png"
|
||||||
|
//
|
||||||
|
// "github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// xlsx := excelize.NewFile()
|
||||||
|
// // Insert a picture.
|
||||||
|
// err := xlsx.AddPicture("Sheet1", "A2", "./image1.jpg", "")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// // Insert a picture scaling in the cell with location hyperlink.
|
||||||
|
// err = xlsx.AddPicture("Sheet1", "D2", "./image1.png", `{"x_scale": 0.5, "y_scale": 0.5, "hyperlink": "#Sheet2!D8", "hyperlink_type": "Location"}`)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// // Insert a picture offset in the cell with external hyperlink, printing and positioning support.
|
||||||
|
// err = xlsx.AddPicture("Sheet1", "H2", "./image3.gif", `{"x_offset": 15, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External", "print_obj": true, "lock_aspect_ratio": false, "locked": false, "positioning": "oneCell"}`)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// err = xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// LinkType defines two types of hyperlink "External" for web site or
|
||||||
|
// "Location" for moving to one of cell in this workbook. When the
|
||||||
|
// "hyperlink_type" is "Location", coordinates need to start with "#".
|
||||||
|
//
|
||||||
|
// Positioning defines two types of the position of a picture in an Excel
|
||||||
|
// spreadsheet, "oneCell" (Move but don't size with cells) or "absolute"
|
||||||
|
// (Don't move or size with cells). If you don't set this parameter, default
|
||||||
|
// positioning is move and size with cells.
|
||||||
|
func (f *File) AddPicture(sheet, cell, picture, format string) error {
|
||||||
|
var err error
|
||||||
|
// Check picture exists first.
|
||||||
|
if _, err = os.Stat(picture); os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ext, ok := supportImageTypes[path.Ext(picture)]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported image extension")
|
||||||
|
}
|
||||||
|
file, _ := ioutil.ReadFile(picture)
|
||||||
|
_, name := filepath.Split(picture)
|
||||||
|
return f.AddPictureFromBytes(sheet, cell, format, name, ext, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPictureFromBytes provides the method to add picture in a sheet by given
|
||||||
|
// picture format set (such as offset, scale, aspect ratio setting and print
|
||||||
|
// settings), file base name, extension name and file bytes. For example:
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
// _ "image/jpeg"
|
||||||
|
// "io/ioutil"
|
||||||
|
//
|
||||||
|
// "github.com/360EntSecGroup-Skylar/excelize"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// xlsx := excelize.NewFile()
|
||||||
|
//
|
||||||
|
// file, err := ioutil.ReadFile("./image1.jpg")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// err = xlsx.AddPictureFromBytes("Sheet1", "A2", "", "Excel Logo", ".jpg", file)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// err = xlsx.SaveAs("./Book1.xlsx")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) AddPictureFromBytes(sheet, cell, format, name, extension string, file []byte) error {
|
||||||
|
var err error
|
||||||
|
var drawingHyperlinkRID int
|
||||||
|
var hyperlinkType string
|
||||||
|
ext, ok := supportImageTypes[extension]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported image extension")
|
||||||
|
}
|
||||||
|
formatSet, err := parseFormatPictureSet(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
image, _, err := image.DecodeConfig(bytes.NewReader(file))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Read sheet data.
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
// Add first picture for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
|
||||||
|
drawingID := f.countDrawings() + 1
|
||||||
|
pictureID := f.countMedia() + 1
|
||||||
|
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
|
||||||
|
drawingID, drawingXML = f.prepareDrawing(xlsx, drawingID, sheet, drawingXML)
|
||||||
|
drawingRID := f.addDrawingRelationships(drawingID, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, hyperlinkType)
|
||||||
|
// Add picture with hyperlink.
|
||||||
|
if formatSet.Hyperlink != "" && formatSet.HyperlinkType != "" {
|
||||||
|
if formatSet.HyperlinkType == "External" {
|
||||||
|
hyperlinkType = formatSet.HyperlinkType
|
||||||
|
}
|
||||||
|
drawingHyperlinkRID = f.addDrawingRelationships(drawingID, SourceRelationshipHyperLink, formatSet.Hyperlink, hyperlinkType)
|
||||||
|
}
|
||||||
|
f.addDrawingPicture(sheet, drawingXML, cell, name, image.Width, image.Height, drawingRID, drawingHyperlinkRID, formatSet)
|
||||||
|
f.addMedia(file, ext)
|
||||||
|
f.addContentTypePart(drawingID, "drawings")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSheetRelationships provides a function to add
|
||||||
|
// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name, relationship
|
||||||
|
// type and target.
|
||||||
|
func (f *File) addSheetRelationships(sheet, relType, target, targetMode string) int {
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
name = strings.ToLower(sheet) + ".xml"
|
||||||
|
}
|
||||||
|
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
||||||
|
var sheetRels xlsxWorkbookRels
|
||||||
|
var rID = 1
|
||||||
|
var ID bytes.Buffer
|
||||||
|
ID.WriteString("rId")
|
||||||
|
ID.WriteString(strconv.Itoa(rID))
|
||||||
|
_, ok = f.XLSX[rels]
|
||||||
|
if ok {
|
||||||
|
ID.Reset()
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rels)), &sheetRels)
|
||||||
|
rID = len(sheetRels.Relationships) + 1
|
||||||
|
ID.WriteString("rId")
|
||||||
|
ID.WriteString(strconv.Itoa(rID))
|
||||||
|
}
|
||||||
|
sheetRels.Relationships = append(sheetRels.Relationships, xlsxWorkbookRelation{
|
||||||
|
ID: ID.String(),
|
||||||
|
Type: relType,
|
||||||
|
Target: target,
|
||||||
|
TargetMode: targetMode,
|
||||||
|
})
|
||||||
|
output, _ := xml.Marshal(sheetRels)
|
||||||
|
f.saveFileList(rels, output)
|
||||||
|
return rID
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSheetRelationships provides a function to delete relationships in
|
||||||
|
// xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
|
||||||
|
// relationship index.
|
||||||
|
func (f *File) deleteSheetRelationships(sheet, rID string) {
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
name = strings.ToLower(sheet) + ".xml"
|
||||||
|
}
|
||||||
|
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
||||||
|
var sheetRels xlsxWorkbookRels
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rels)), &sheetRels)
|
||||||
|
for k, v := range sheetRels.Relationships {
|
||||||
|
if v.ID == rID {
|
||||||
|
sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output, _ := xml.Marshal(sheetRels)
|
||||||
|
f.saveFileList(rels, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSheetLegacyDrawing provides a function to add legacy drawing element to
|
||||||
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||||
|
func (f *File) addSheetLegacyDrawing(sheet string, rID int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
xlsx.LegacyDrawing = &xlsxLegacyDrawing{
|
||||||
|
RID: "rId" + strconv.Itoa(rID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSheetDrawing provides a function to add drawing element to
|
||||||
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||||
|
func (f *File) addSheetDrawing(sheet string, rID int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
xlsx.Drawing = &xlsxDrawing{
|
||||||
|
RID: "rId" + strconv.Itoa(rID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSheetPicture provides a function to add picture element to
|
||||||
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||||
|
func (f *File) addSheetPicture(sheet string, rID int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
xlsx.Picture = &xlsxPicture{
|
||||||
|
RID: "rId" + strconv.Itoa(rID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// countDrawings provides a function to get drawing files count storage in the
|
||||||
|
// folder xl/drawings.
|
||||||
|
func (f *File) countDrawings() int {
|
||||||
|
count := 0
|
||||||
|
for k := range f.XLSX {
|
||||||
|
if strings.Contains(k, "xl/drawings/drawing") {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDrawingPicture provides a function to add picture by given sheet,
|
||||||
|
// drawingXML, cell, file name, width, height relationship index and format
|
||||||
|
// sets.
|
||||||
|
func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, height, rID, hyperlinkRID int, formatSet *formatPicture) {
|
||||||
|
cell = strings.ToUpper(cell)
|
||||||
|
fromCol := string(strings.Map(letterOnlyMapF, cell))
|
||||||
|
fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
|
||||||
|
row := fromRow - 1
|
||||||
|
col := TitleToNumber(fromCol)
|
||||||
|
width = int(float64(width) * formatSet.XScale)
|
||||||
|
height = int(float64(height) * formatSet.YScale)
|
||||||
|
colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, formatSet.OffsetX, formatSet.OffsetY, width, height)
|
||||||
|
content := xlsxWsDr{}
|
||||||
|
content.A = NameSpaceDrawingML
|
||||||
|
content.Xdr = NameSpaceDrawingMLSpreadSheet
|
||||||
|
cNvPrID := f.drawingParser(drawingXML, &content)
|
||||||
|
twoCellAnchor := xdrCellAnchor{}
|
||||||
|
twoCellAnchor.EditAs = formatSet.Positioning
|
||||||
|
from := xlsxFrom{}
|
||||||
|
from.Col = colStart
|
||||||
|
from.ColOff = formatSet.OffsetX * EMU
|
||||||
|
from.Row = rowStart
|
||||||
|
from.RowOff = formatSet.OffsetY * EMU
|
||||||
|
to := xlsxTo{}
|
||||||
|
to.Col = colEnd
|
||||||
|
to.ColOff = x2 * EMU
|
||||||
|
to.Row = rowEnd
|
||||||
|
to.RowOff = y2 * EMU
|
||||||
|
twoCellAnchor.From = &from
|
||||||
|
twoCellAnchor.To = &to
|
||||||
|
pic := xlsxPic{}
|
||||||
|
pic.NvPicPr.CNvPicPr.PicLocks.NoChangeAspect = formatSet.NoChangeAspect
|
||||||
|
pic.NvPicPr.CNvPr.ID = f.countCharts() + f.countMedia() + 1
|
||||||
|
pic.NvPicPr.CNvPr.Descr = file
|
||||||
|
pic.NvPicPr.CNvPr.Name = "Picture " + strconv.Itoa(cNvPrID)
|
||||||
|
if hyperlinkRID != 0 {
|
||||||
|
pic.NvPicPr.CNvPr.HlinkClick = &xlsxHlinkClick{
|
||||||
|
R: SourceRelationship,
|
||||||
|
RID: "rId" + strconv.Itoa(hyperlinkRID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pic.BlipFill.Blip.R = SourceRelationship
|
||||||
|
pic.BlipFill.Blip.Embed = "rId" + strconv.Itoa(rID)
|
||||||
|
pic.SpPr.PrstGeom.Prst = "rect"
|
||||||
|
|
||||||
|
twoCellAnchor.Pic = &pic
|
||||||
|
twoCellAnchor.ClientData = &xdrClientData{
|
||||||
|
FLocksWithSheet: formatSet.FLocksWithSheet,
|
||||||
|
FPrintsWithSheet: formatSet.FPrintsWithSheet,
|
||||||
|
}
|
||||||
|
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
||||||
|
output, _ := xml.Marshal(content)
|
||||||
|
f.saveFileList(drawingXML, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDrawingRelationships provides a function to add image part relationships
|
||||||
|
// in the file xl/drawings/_rels/drawing%d.xml.rels by given drawing index,
|
||||||
|
// relationship type and target.
|
||||||
|
func (f *File) addDrawingRelationships(index int, relType, target, targetMode string) int {
|
||||||
|
var rels = "xl/drawings/_rels/drawing" + strconv.Itoa(index) + ".xml.rels"
|
||||||
|
var drawingRels xlsxWorkbookRels
|
||||||
|
var rID = 1
|
||||||
|
var ID bytes.Buffer
|
||||||
|
ID.WriteString("rId")
|
||||||
|
ID.WriteString(strconv.Itoa(rID))
|
||||||
|
_, ok := f.XLSX[rels]
|
||||||
|
if ok {
|
||||||
|
ID.Reset()
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rels)), &drawingRels)
|
||||||
|
rID = len(drawingRels.Relationships) + 1
|
||||||
|
ID.WriteString("rId")
|
||||||
|
ID.WriteString(strconv.Itoa(rID))
|
||||||
|
}
|
||||||
|
drawingRels.Relationships = append(drawingRels.Relationships, xlsxWorkbookRelation{
|
||||||
|
ID: ID.String(),
|
||||||
|
Type: relType,
|
||||||
|
Target: target,
|
||||||
|
TargetMode: targetMode,
|
||||||
|
})
|
||||||
|
output, _ := xml.Marshal(drawingRels)
|
||||||
|
f.saveFileList(rels, output)
|
||||||
|
return rID
|
||||||
|
}
|
||||||
|
|
||||||
|
// countMedia provides a function to get media files count storage in the
|
||||||
|
// folder xl/media/image.
|
||||||
|
func (f *File) countMedia() int {
|
||||||
|
count := 0
|
||||||
|
for k := range f.XLSX {
|
||||||
|
if strings.Contains(k, "xl/media/image") {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMedia provides a function to add picture into folder xl/media/image by
|
||||||
|
// given file and extension name.
|
||||||
|
func (f *File) addMedia(file []byte, ext string) {
|
||||||
|
count := f.countMedia()
|
||||||
|
media := "xl/media/image" + strconv.Itoa(count+1) + ext
|
||||||
|
f.XLSX[media] = file
|
||||||
|
}
|
||||||
|
|
||||||
|
// setContentTypePartImageExtensions provides a function to set the content
|
||||||
|
// type for relationship parts and the Main Document part.
|
||||||
|
func (f *File) setContentTypePartImageExtensions() {
|
||||||
|
var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false}
|
||||||
|
content := f.contentTypesReader()
|
||||||
|
for _, v := range content.Defaults {
|
||||||
|
_, ok := imageTypes[v.Extension]
|
||||||
|
if ok {
|
||||||
|
imageTypes[v.Extension] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range imageTypes {
|
||||||
|
if !v {
|
||||||
|
content.Defaults = append(content.Defaults, xlsxDefault{
|
||||||
|
Extension: k,
|
||||||
|
ContentType: "image/" + k,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setContentTypePartVMLExtensions provides a function to set the content type
|
||||||
|
// for relationship parts and the Main Document part.
|
||||||
|
func (f *File) setContentTypePartVMLExtensions() {
|
||||||
|
vml := false
|
||||||
|
content := f.contentTypesReader()
|
||||||
|
for _, v := range content.Defaults {
|
||||||
|
if v.Extension == "vml" {
|
||||||
|
vml = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !vml {
|
||||||
|
content.Defaults = append(content.Defaults, xlsxDefault{
|
||||||
|
Extension: "vml",
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.vmlDrawing",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addContentTypePart provides a function to add content type part
|
||||||
|
// relationships in the file [Content_Types].xml by given index.
|
||||||
|
func (f *File) addContentTypePart(index int, contentType string) {
|
||||||
|
setContentType := map[string]func(){
|
||||||
|
"comments": f.setContentTypePartVMLExtensions,
|
||||||
|
"drawings": f.setContentTypePartImageExtensions,
|
||||||
|
}
|
||||||
|
partNames := map[string]string{
|
||||||
|
"chart": "/xl/charts/chart" + strconv.Itoa(index) + ".xml",
|
||||||
|
"comments": "/xl/comments" + strconv.Itoa(index) + ".xml",
|
||||||
|
"drawings": "/xl/drawings/drawing" + strconv.Itoa(index) + ".xml",
|
||||||
|
"table": "/xl/tables/table" + strconv.Itoa(index) + ".xml",
|
||||||
|
}
|
||||||
|
contentTypes := map[string]string{
|
||||||
|
"chart": "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
|
||||||
|
"comments": "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
|
||||||
|
"drawings": "application/vnd.openxmlformats-officedocument.drawing+xml",
|
||||||
|
"table": "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml",
|
||||||
|
}
|
||||||
|
s, ok := setContentType[contentType]
|
||||||
|
if ok {
|
||||||
|
s()
|
||||||
|
}
|
||||||
|
content := f.contentTypesReader()
|
||||||
|
for _, v := range content.Overrides {
|
||||||
|
if v.PartName == partNames[contentType] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
||||||
|
PartName: partNames[contentType],
|
||||||
|
ContentType: contentTypes[contentType],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSheetRelationshipsTargetByID provides a function to get Target attribute
|
||||||
|
// value in xl/worksheets/_rels/sheet%d.xml.rels by given worksheet name and
|
||||||
|
// relationship index.
|
||||||
|
func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
name = strings.ToLower(sheet) + ".xml"
|
||||||
|
}
|
||||||
|
var rels = "xl/worksheets/_rels/" + strings.TrimPrefix(name, "xl/worksheets/") + ".rels"
|
||||||
|
var sheetRels xlsxWorkbookRels
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rels)), &sheetRels)
|
||||||
|
for _, v := range sheetRels.Relationships {
|
||||||
|
if v.ID == rID {
|
||||||
|
return v.Target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPicture provides a function to get picture base name and raw content
|
||||||
|
// embed in XLSX by given worksheet and cell name. This function returns the
|
||||||
|
// file name in XLSX and file contents as []byte data types. For example:
|
||||||
|
//
|
||||||
|
// xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// file, raw := xlsx.GetPicture("Sheet1", "A2")
|
||||||
|
// if file == "" {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// err := ioutil.WriteFile(file, raw, 0644)
|
||||||
|
// if err != nil {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) GetPicture(sheet, cell string) (string, []byte) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.Drawing == nil {
|
||||||
|
return "", []byte{}
|
||||||
|
}
|
||||||
|
target := f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
|
||||||
|
drawingXML := strings.Replace(target, "..", "xl", -1)
|
||||||
|
|
||||||
|
_, ok := f.XLSX[drawingXML]
|
||||||
|
if !ok {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
decodeWsDr := decodeWsDr{}
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(drawingXML)), &decodeWsDr)
|
||||||
|
|
||||||
|
cell = strings.ToUpper(cell)
|
||||||
|
fromCol := string(strings.Map(letterOnlyMapF, cell))
|
||||||
|
fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
|
||||||
|
row := fromRow - 1
|
||||||
|
col := TitleToNumber(fromCol)
|
||||||
|
|
||||||
|
drawingRelationships := strings.Replace(strings.Replace(target, "../drawings", "xl/drawings/_rels", -1), ".xml", ".xml.rels", -1)
|
||||||
|
|
||||||
|
for _, anchor := range decodeWsDr.TwoCellAnchor {
|
||||||
|
decodeTwoCellAnchor := decodeTwoCellAnchor{}
|
||||||
|
_ = xml.Unmarshal([]byte("<decodeTwoCellAnchor>"+anchor.Content+"</decodeTwoCellAnchor>"), &decodeTwoCellAnchor)
|
||||||
|
if decodeTwoCellAnchor.From != nil && decodeTwoCellAnchor.Pic != nil {
|
||||||
|
if decodeTwoCellAnchor.From.Col == col && decodeTwoCellAnchor.From.Row == row {
|
||||||
|
xlsxWorkbookRelation := f.getDrawingRelationships(drawingRelationships, decodeTwoCellAnchor.Pic.BlipFill.Blip.Embed)
|
||||||
|
_, ok := supportImageTypes[filepath.Ext(xlsxWorkbookRelation.Target)]
|
||||||
|
if ok {
|
||||||
|
return filepath.Base(xlsxWorkbookRelation.Target), []byte(f.XLSX[strings.Replace(xlsxWorkbookRelation.Target, "..", "xl", -1)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDrawingRelationships provides a function to get drawing relationships
|
||||||
|
// from xl/drawings/_rels/drawing%s.xml.rels by given file name and
|
||||||
|
// relationship ID.
|
||||||
|
func (f *File) getDrawingRelationships(rels, rID string) *xlsxWorkbookRelation {
|
||||||
|
_, ok := f.XLSX[rels]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var drawingRels xlsxWorkbookRels
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML(rels)), &drawingRels)
|
||||||
|
for _, v := range drawingRels.Relationships {
|
||||||
|
if v.ID == rID {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
510
vendor/github.com/360EntSecGroup-Skylar/excelize/rows.go
generated
vendored
Normal file
510
vendor/github.com/360EntSecGroup-Skylar/excelize/rows.go
generated
vendored
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetRows return all the rows in a sheet by given worksheet name (case
|
||||||
|
// sensitive). For example:
|
||||||
|
//
|
||||||
|
// for _, row := range xlsx.GetRows("Sheet1") {
|
||||||
|
// for _, colCell := range row {
|
||||||
|
// fmt.Print(colCell, "\t")
|
||||||
|
// }
|
||||||
|
// fmt.Println()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) GetRows(sheet string) [][]string {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
return [][]string{}
|
||||||
|
}
|
||||||
|
if xlsx != nil {
|
||||||
|
output, _ := xml.Marshal(f.Sheet[name])
|
||||||
|
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
|
||||||
|
}
|
||||||
|
xml.NewDecoder(bytes.NewReader(f.readXML(name)))
|
||||||
|
d := f.sharedStringsReader()
|
||||||
|
var inElement string
|
||||||
|
var r xlsxRow
|
||||||
|
tr, tc := f.getTotalRowsCols(name)
|
||||||
|
rows := make([][]string, tr)
|
||||||
|
for i := range rows {
|
||||||
|
rows[i] = make([]string, tc+1)
|
||||||
|
}
|
||||||
|
var row int
|
||||||
|
decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
|
||||||
|
for {
|
||||||
|
token, _ := decoder.Token()
|
||||||
|
if token == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch startElement := token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
inElement = startElement.Name.Local
|
||||||
|
if inElement == "row" {
|
||||||
|
r = xlsxRow{}
|
||||||
|
_ = decoder.DecodeElement(&r, &startElement)
|
||||||
|
cr := r.R - 1
|
||||||
|
for _, colCell := range r.C {
|
||||||
|
c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
|
||||||
|
val, _ := colCell.getValueFrom(f, d)
|
||||||
|
rows[cr][c] = val
|
||||||
|
if val != "" {
|
||||||
|
row = r.R
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows[:row]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rows defines an iterator to a sheet
|
||||||
|
type Rows struct {
|
||||||
|
decoder *xml.Decoder
|
||||||
|
token xml.Token
|
||||||
|
err error
|
||||||
|
f *File
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next will return true if find the next row element.
|
||||||
|
func (rows *Rows) Next() bool {
|
||||||
|
for {
|
||||||
|
rows.token, rows.err = rows.decoder.Token()
|
||||||
|
if rows.err == io.EOF {
|
||||||
|
rows.err = nil
|
||||||
|
}
|
||||||
|
if rows.token == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch startElement := rows.token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
inElement := startElement.Name.Local
|
||||||
|
if inElement == "row" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error will return the error when the find next row element
|
||||||
|
func (rows *Rows) Error() error {
|
||||||
|
return rows.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns return the current row's column values
|
||||||
|
func (rows *Rows) Columns() []string {
|
||||||
|
if rows.token == nil {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
startElement := rows.token.(xml.StartElement)
|
||||||
|
r := xlsxRow{}
|
||||||
|
_ = rows.decoder.DecodeElement(&r, &startElement)
|
||||||
|
d := rows.f.sharedStringsReader()
|
||||||
|
row := make([]string, len(r.C))
|
||||||
|
for _, colCell := range r.C {
|
||||||
|
c := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
|
||||||
|
val, _ := colCell.getValueFrom(rows.f, d)
|
||||||
|
row[c] = val
|
||||||
|
}
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrSheetNotExist defines an error of sheet is not exist
|
||||||
|
type ErrSheetNotExist struct {
|
||||||
|
SheetName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrSheetNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("Sheet %s is not exist", string(err.SheetName))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rows return a rows iterator. For example:
|
||||||
|
//
|
||||||
|
// rows, err := xlsx.Rows("Sheet1")
|
||||||
|
// for rows.Next() {
|
||||||
|
// for _, colCell := range rows.Columns() {
|
||||||
|
// fmt.Print(colCell, "\t")
|
||||||
|
// }
|
||||||
|
// fmt.Println()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) Rows(sheet string) (*Rows, error) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrSheetNotExist{sheet}
|
||||||
|
}
|
||||||
|
if xlsx != nil {
|
||||||
|
output, _ := xml.Marshal(f.Sheet[name])
|
||||||
|
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
|
||||||
|
}
|
||||||
|
return &Rows{
|
||||||
|
f: f,
|
||||||
|
decoder: xml.NewDecoder(bytes.NewReader(f.readXML(name))),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTotalRowsCols provides a function to get total columns and rows in a
|
||||||
|
// worksheet.
|
||||||
|
func (f *File) getTotalRowsCols(name string) (int, int) {
|
||||||
|
decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
|
||||||
|
var inElement string
|
||||||
|
var r xlsxRow
|
||||||
|
var tr, tc int
|
||||||
|
for {
|
||||||
|
token, _ := decoder.Token()
|
||||||
|
if token == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch startElement := token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
inElement = startElement.Name.Local
|
||||||
|
if inElement == "row" {
|
||||||
|
r = xlsxRow{}
|
||||||
|
_ = decoder.DecodeElement(&r, &startElement)
|
||||||
|
tr = r.R
|
||||||
|
for _, colCell := range r.C {
|
||||||
|
col := TitleToNumber(strings.Map(letterOnlyMapF, colCell.R))
|
||||||
|
if col > tc {
|
||||||
|
tc = col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tr, tc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRowHeight provides a function to set the height of a single row. For
|
||||||
|
// example, set the height of the first row in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetRowHeight("Sheet1", 1, 50)
|
||||||
|
//
|
||||||
|
func (f *File) SetRowHeight(sheet string, row int, height float64) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
cells := 0
|
||||||
|
rowIdx := row - 1
|
||||||
|
completeRow(xlsx, row, cells)
|
||||||
|
xlsx.SheetData.Row[rowIdx].Ht = height
|
||||||
|
xlsx.SheetData.Row[rowIdx].CustomHeight = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRowHeight provides a function to get row height in pixels by given sheet
|
||||||
|
// name and row index.
|
||||||
|
func (f *File) getRowHeight(sheet string, row int) int {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
for _, v := range xlsx.SheetData.Row {
|
||||||
|
if v.R == row+1 && v.Ht != 0 {
|
||||||
|
return int(convertRowHeightToPixels(v.Ht))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Optimisation for when the row heights haven't changed.
|
||||||
|
return int(defaultRowHeightPixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRowHeight provides a function to get row height by given worksheet name
|
||||||
|
// and row index. For example, get the height of the first row in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetRowHeight("Sheet1", 1)
|
||||||
|
//
|
||||||
|
func (f *File) GetRowHeight(sheet string, row int) float64 {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
for _, v := range xlsx.SheetData.Row {
|
||||||
|
if v.R == row && v.Ht != 0 {
|
||||||
|
return v.Ht
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Optimisation for when the row heights haven't changed.
|
||||||
|
return defaultRowHeightPixels
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharedStringsReader provides a function to get the pointer to the structure
|
||||||
|
// after deserialization of xl/sharedStrings.xml.
|
||||||
|
func (f *File) sharedStringsReader() *xlsxSST {
|
||||||
|
if f.SharedStrings == nil {
|
||||||
|
var sharedStrings xlsxSST
|
||||||
|
ss := f.readXML("xl/sharedStrings.xml")
|
||||||
|
if len(ss) == 0 {
|
||||||
|
ss = f.readXML("xl/SharedStrings.xml")
|
||||||
|
}
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(ss), &sharedStrings)
|
||||||
|
f.SharedStrings = &sharedStrings
|
||||||
|
}
|
||||||
|
return f.SharedStrings
|
||||||
|
}
|
||||||
|
|
||||||
|
// getValueFrom return a value from a column/row cell, this function is
|
||||||
|
// inteded to be used with for range on rows an argument with the xlsx opened
|
||||||
|
// file.
|
||||||
|
func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
|
||||||
|
switch xlsx.T {
|
||||||
|
case "s":
|
||||||
|
xlsxSI := 0
|
||||||
|
xlsxSI, _ = strconv.Atoi(xlsx.V)
|
||||||
|
if len(d.SI[xlsxSI].R) > 0 {
|
||||||
|
value := ""
|
||||||
|
for _, v := range d.SI[xlsxSI].R {
|
||||||
|
value += v.T
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
return f.formattedValue(xlsx.S, d.SI[xlsxSI].T), nil
|
||||||
|
case "str":
|
||||||
|
return f.formattedValue(xlsx.S, xlsx.V), nil
|
||||||
|
case "inlineStr":
|
||||||
|
return f.formattedValue(xlsx.S, xlsx.IS.T), nil
|
||||||
|
default:
|
||||||
|
return f.formattedValue(xlsx.S, xlsx.V), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRowVisible provides a function to set visible of a single row by given
|
||||||
|
// worksheet name and row index. For example, hide row 2 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetRowVisible("Sheet1", 2, false)
|
||||||
|
//
|
||||||
|
func (f *File) SetRowVisible(sheet string, rowIndex int, visible bool) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
rows := rowIndex + 1
|
||||||
|
cells := 0
|
||||||
|
completeRow(xlsx, rows, cells)
|
||||||
|
if visible {
|
||||||
|
xlsx.SheetData.Row[rowIndex].Hidden = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[rowIndex].Hidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRowVisible provides a function to get visible of a single row by given
|
||||||
|
// worksheet name and row index. For example, get visible state of row 2 in
|
||||||
|
// Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetRowVisible("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) GetRowVisible(sheet string, rowIndex int) bool {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
rows := rowIndex + 1
|
||||||
|
cells := 0
|
||||||
|
completeRow(xlsx, rows, cells)
|
||||||
|
return !xlsx.SheetData.Row[rowIndex].Hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRowOutlineLevel provides a function to set outline level number of a
|
||||||
|
// single row by given worksheet name and row index. For example, outline row
|
||||||
|
// 2 in Sheet1 to level 1:
|
||||||
|
//
|
||||||
|
// xlsx.SetRowOutlineLevel("Sheet1", 2, 1)
|
||||||
|
//
|
||||||
|
func (f *File) SetRowOutlineLevel(sheet string, rowIndex int, level uint8) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
rows := rowIndex + 1
|
||||||
|
cells := 0
|
||||||
|
completeRow(xlsx, rows, cells)
|
||||||
|
xlsx.SheetData.Row[rowIndex].OutlineLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRowOutlineLevel provides a function to get outline level number of a
|
||||||
|
// single row by given worksheet name and row index. For example, get outline
|
||||||
|
// number of row 2 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetRowOutlineLevel("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) GetRowOutlineLevel(sheet string, rowIndex int) uint8 {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
rows := rowIndex + 1
|
||||||
|
cells := 0
|
||||||
|
completeRow(xlsx, rows, cells)
|
||||||
|
return xlsx.SheetData.Row[rowIndex].OutlineLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveRow provides a function to remove single row by given worksheet name
|
||||||
|
// and row index. For example, remove row 3 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.RemoveRow("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) RemoveRow(sheet string, row int) {
|
||||||
|
if row < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
row++
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if r.R == row {
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row[:i], xlsx.SheetData.Row[i+1:]...)
|
||||||
|
f.adjustHelper(sheet, -1, row, -1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertRow provides a function to insert a new row after given row index.
|
||||||
|
// For example, create a new row before row 3 in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.InsertRow("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) InsertRow(sheet string, row int) {
|
||||||
|
if row < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
row++
|
||||||
|
f.adjustHelper(sheet, -1, row, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DuplicateRow inserts a copy of specified row below specified
|
||||||
|
//
|
||||||
|
// xlsx.DuplicateRow("Sheet1", 2)
|
||||||
|
//
|
||||||
|
func (f *File) DuplicateRow(sheet string, row int) {
|
||||||
|
if row < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
row2 := row + 1
|
||||||
|
f.adjustHelper(sheet, -1, row2, 1)
|
||||||
|
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
idx := -1
|
||||||
|
idx2 := -1
|
||||||
|
|
||||||
|
for i, r := range xlsx.SheetData.Row {
|
||||||
|
if r.R == row {
|
||||||
|
idx = i
|
||||||
|
} else if r.R == row2 {
|
||||||
|
idx2 = i
|
||||||
|
}
|
||||||
|
if idx != -1 && idx2 != -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx == -1 || (idx2 == -1 && len(xlsx.SheetData.Row) >= row2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rowData := xlsx.SheetData.Row[idx]
|
||||||
|
cols := make([]xlsxC, 0, len(rowData.C))
|
||||||
|
rowData.C = append(cols, rowData.C...)
|
||||||
|
f.ajustSingleRowDimensions(&rowData, 1)
|
||||||
|
if idx2 != -1 {
|
||||||
|
xlsx.SheetData.Row[idx2] = rowData
|
||||||
|
} else {
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row, rowData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRow provides a function to check and fill each column element for all
|
||||||
|
// rows and make that is continuous in a worksheet of XML. For example:
|
||||||
|
//
|
||||||
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
||||||
|
// <c r="A15" s="2" />
|
||||||
|
// <c r="B15" s="2" />
|
||||||
|
// <c r="F15" s="1" />
|
||||||
|
// <c r="G15" s="1" />
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
// in this case, we should to change it to
|
||||||
|
//
|
||||||
|
// <row r="15" spans="1:22" x14ac:dyDescent="0.2">
|
||||||
|
// <c r="A15" s="2" />
|
||||||
|
// <c r="B15" s="2" />
|
||||||
|
// <c r="C15" s="2" />
|
||||||
|
// <c r="D15" s="2" />
|
||||||
|
// <c r="E15" s="2" />
|
||||||
|
// <c r="F15" s="1" />
|
||||||
|
// <c r="G15" s="1" />
|
||||||
|
// </row>
|
||||||
|
//
|
||||||
|
// Noteice: this method could be very slow for large spreadsheets (more than
|
||||||
|
// 3000 rows one sheet).
|
||||||
|
func checkRow(xlsx *xlsxWorksheet) {
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for k := range xlsx.SheetData.Row {
|
||||||
|
lenCol := len(xlsx.SheetData.Row[k].C)
|
||||||
|
if lenCol > 0 {
|
||||||
|
endR := string(strings.Map(letterOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R))
|
||||||
|
endRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, xlsx.SheetData.Row[k].C[lenCol-1].R))
|
||||||
|
endCol := TitleToNumber(endR) + 1
|
||||||
|
if lenCol < endCol {
|
||||||
|
oldRow := xlsx.SheetData.Row[k].C
|
||||||
|
xlsx.SheetData.Row[k].C = xlsx.SheetData.Row[k].C[:0]
|
||||||
|
tmp := []xlsxC{}
|
||||||
|
for i := 0; i < endCol; i++ {
|
||||||
|
buffer.WriteString(ToAlphaString(i))
|
||||||
|
buffer.WriteString(strconv.Itoa(endRow))
|
||||||
|
tmp = append(tmp, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
xlsx.SheetData.Row[k].C = tmp
|
||||||
|
for _, y := range oldRow {
|
||||||
|
colAxis := TitleToNumber(string(strings.Map(letterOnlyMapF, y.R)))
|
||||||
|
xlsx.SheetData.Row[k].C[colAxis] = y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// completeRow provides a function to check and fill each column element for a
|
||||||
|
// single row and make that is continuous in a worksheet of XML by given row
|
||||||
|
// index and axis.
|
||||||
|
func completeRow(xlsx *xlsxWorksheet, row, cell int) {
|
||||||
|
currentRows := len(xlsx.SheetData.Row)
|
||||||
|
if currentRows > 1 {
|
||||||
|
lastRow := xlsx.SheetData.Row[currentRows-1].R
|
||||||
|
if lastRow >= row {
|
||||||
|
row = lastRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := currentRows; i < row; i++ {
|
||||||
|
xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{
|
||||||
|
R: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
for ii := currentRows; ii < row; ii++ {
|
||||||
|
start := len(xlsx.SheetData.Row[ii].C)
|
||||||
|
if start == 0 {
|
||||||
|
for iii := start; iii < cell; iii++ {
|
||||||
|
buffer.WriteString(ToAlphaString(iii))
|
||||||
|
buffer.WriteString(strconv.Itoa(ii + 1))
|
||||||
|
xlsx.SheetData.Row[ii].C = append(xlsx.SheetData.Row[ii].C, xlsxC{
|
||||||
|
R: buffer.String(),
|
||||||
|
})
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertRowHeightToPixels provides a function to convert the height of a
|
||||||
|
// cell from user's units to pixels. If the height hasn't been set by the user
|
||||||
|
// we use the default value. If the row is hidden it has a value of zero.
|
||||||
|
func convertRowHeightToPixels(height float64) float64 {
|
||||||
|
var pixels float64
|
||||||
|
if height == 0 {
|
||||||
|
return pixels
|
||||||
|
}
|
||||||
|
pixels = math.Ceil(4.0 / 3.0 * height)
|
||||||
|
return pixels
|
||||||
|
}
|
||||||
428
vendor/github.com/360EntSecGroup-Skylar/excelize/shape.go
generated
vendored
Normal file
428
vendor/github.com/360EntSecGroup-Skylar/excelize/shape.go
generated
vendored
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseFormatShapeSet provides a function to parse the format settings of the
|
||||||
|
// shape with default value.
|
||||||
|
func parseFormatShapeSet(formatSet string) (*formatShape, error) {
|
||||||
|
format := formatShape{
|
||||||
|
Width: 160,
|
||||||
|
Height: 160,
|
||||||
|
Format: formatPicture{
|
||||||
|
FPrintsWithSheet: true,
|
||||||
|
FLocksWithSheet: false,
|
||||||
|
NoChangeAspect: false,
|
||||||
|
OffsetX: 0,
|
||||||
|
OffsetY: 0,
|
||||||
|
XScale: 1.0,
|
||||||
|
YScale: 1.0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := json.Unmarshal([]byte(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddShape provides the method to add shape in a sheet by given worksheet
|
||||||
|
// index, shape format set (such as offset, scale, aspect ratio setting and
|
||||||
|
// print settings) and properties set. For example, add text box (rect shape)
|
||||||
|
// in Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.AddShape("Sheet1", "G6", `{"type":"rect","color":{"line":"#4286F4","fill":"#8eb9ff"},"paragraph":[{"text":"Rectangle Shape","font":{"bold":true,"italic":true,"family":"Berlin Sans FB Demi","size":36,"color":"#777777","underline":"sng"}}],"width":180,"height": 90}`)
|
||||||
|
//
|
||||||
|
// The following shows the type of shape supported by excelize:
|
||||||
|
//
|
||||||
|
// accentBorderCallout1 (Callout 1 with Border and Accent Shape)
|
||||||
|
// accentBorderCallout2 (Callout 2 with Border and Accent Shape)
|
||||||
|
// accentBorderCallout3 (Callout 3 with Border and Accent Shape)
|
||||||
|
// accentCallout1 (Callout 1 Shape)
|
||||||
|
// accentCallout2 (Callout 2 Shape)
|
||||||
|
// accentCallout3 (Callout 3 Shape)
|
||||||
|
// actionButtonBackPrevious (Back or Previous Button Shape)
|
||||||
|
// actionButtonBeginning (Beginning Button Shape)
|
||||||
|
// actionButtonBlank (Blank Button Shape)
|
||||||
|
// actionButtonDocument (Document Button Shape)
|
||||||
|
// actionButtonEnd (End Button Shape)
|
||||||
|
// actionButtonForwardNext (Forward or Next Button Shape)
|
||||||
|
// actionButtonHelp (Help Button Shape)
|
||||||
|
// actionButtonHome (Home Button Shape)
|
||||||
|
// actionButtonInformation (Information Button Shape)
|
||||||
|
// actionButtonMovie (Movie Button Shape)
|
||||||
|
// actionButtonReturn (Return Button Shape)
|
||||||
|
// actionButtonSound (Sound Button Shape)
|
||||||
|
// arc (Curved Arc Shape)
|
||||||
|
// bentArrow (Bent Arrow Shape)
|
||||||
|
// bentConnector2 (Bent Connector 2 Shape)
|
||||||
|
// bentConnector3 (Bent Connector 3 Shape)
|
||||||
|
// bentConnector4 (Bent Connector 4 Shape)
|
||||||
|
// bentConnector5 (Bent Connector 5 Shape)
|
||||||
|
// bentUpArrow (Bent Up Arrow Shape)
|
||||||
|
// bevel (Bevel Shape)
|
||||||
|
// blockArc (Block Arc Shape)
|
||||||
|
// borderCallout1 (Callout 1 with Border Shape)
|
||||||
|
// borderCallout2 (Callout 2 with Border Shape)
|
||||||
|
// borderCallout3 (Callout 3 with Border Shape)
|
||||||
|
// bracePair (Brace Pair Shape)
|
||||||
|
// bracketPair (Bracket Pair Shape)
|
||||||
|
// callout1 (Callout 1 Shape)
|
||||||
|
// callout2 (Callout 2 Shape)
|
||||||
|
// callout3 (Callout 3 Shape)
|
||||||
|
// can (Can Shape)
|
||||||
|
// chartPlus (Chart Plus Shape)
|
||||||
|
// chartStar (Chart Star Shape)
|
||||||
|
// chartX (Chart X Shape)
|
||||||
|
// chevron (Chevron Shape)
|
||||||
|
// chord (Chord Shape)
|
||||||
|
// circularArrow (Circular Arrow Shape)
|
||||||
|
// cloud (Cloud Shape)
|
||||||
|
// cloudCallout (Callout Cloud Shape)
|
||||||
|
// corner (Corner Shape)
|
||||||
|
// cornerTabs (Corner Tabs Shape)
|
||||||
|
// cube (Cube Shape)
|
||||||
|
// curvedConnector2 (Curved Connector 2 Shape)
|
||||||
|
// curvedConnector3 (Curved Connector 3 Shape)
|
||||||
|
// curvedConnector4 (Curved Connector 4 Shape)
|
||||||
|
// curvedConnector5 (Curved Connector 5 Shape)
|
||||||
|
// curvedDownArrow (Curved Down Arrow Shape)
|
||||||
|
// curvedLeftArrow (Curved Left Arrow Shape)
|
||||||
|
// curvedRightArrow (Curved Right Arrow Shape)
|
||||||
|
// curvedUpArrow (Curved Up Arrow Shape)
|
||||||
|
// decagon (Decagon Shape)
|
||||||
|
// diagStripe (Diagonal Stripe Shape)
|
||||||
|
// diamond (Diamond Shape)
|
||||||
|
// dodecagon (Dodecagon Shape)
|
||||||
|
// donut (Donut Shape)
|
||||||
|
// doubleWave (Double Wave Shape)
|
||||||
|
// downArrow (Down Arrow Shape)
|
||||||
|
// downArrowCallout (Callout Down Arrow Shape)
|
||||||
|
// ellipse (Ellipse Shape)
|
||||||
|
// ellipseRibbon (Ellipse Ribbon Shape)
|
||||||
|
// ellipseRibbon2 (Ellipse Ribbon 2 Shape)
|
||||||
|
// flowChartAlternateProcess (Alternate Process Flow Shape)
|
||||||
|
// flowChartCollate (Collate Flow Shape)
|
||||||
|
// flowChartConnector (Connector Flow Shape)
|
||||||
|
// flowChartDecision (Decision Flow Shape)
|
||||||
|
// flowChartDelay (Delay Flow Shape)
|
||||||
|
// flowChartDisplay (Display Flow Shape)
|
||||||
|
// flowChartDocument (Document Flow Shape)
|
||||||
|
// flowChartExtract (Extract Flow Shape)
|
||||||
|
// flowChartInputOutput (Input Output Flow Shape)
|
||||||
|
// flowChartInternalStorage (Internal Storage Flow Shape)
|
||||||
|
// flowChartMagneticDisk (Magnetic Disk Flow Shape)
|
||||||
|
// flowChartMagneticDrum (Magnetic Drum Flow Shape)
|
||||||
|
// flowChartMagneticTape (Magnetic Tape Flow Shape)
|
||||||
|
// flowChartManualInput (Manual Input Flow Shape)
|
||||||
|
// flowChartManualOperation (Manual Operation Flow Shape)
|
||||||
|
// flowChartMerge (Merge Flow Shape)
|
||||||
|
// flowChartMultidocument (Multi-Document Flow Shape)
|
||||||
|
// flowChartOfflineStorage (Offline Storage Flow Shape)
|
||||||
|
// flowChartOffpageConnector (Off-Page Connector Flow Shape)
|
||||||
|
// flowChartOnlineStorage (Online Storage Flow Shape)
|
||||||
|
// flowChartOr (Or Flow Shape)
|
||||||
|
// flowChartPredefinedProcess (Predefined Process Flow Shape)
|
||||||
|
// flowChartPreparation (Preparation Flow Shape)
|
||||||
|
// flowChartProcess (Process Flow Shape)
|
||||||
|
// flowChartPunchedCard (Punched Card Flow Shape)
|
||||||
|
// flowChartPunchedTape (Punched Tape Flow Shape)
|
||||||
|
// flowChartSort (Sort Flow Shape)
|
||||||
|
// flowChartSummingJunction (Summing Junction Flow Shape)
|
||||||
|
// flowChartTerminator (Terminator Flow Shape)
|
||||||
|
// foldedCorner (Folded Corner Shape)
|
||||||
|
// frame (Frame Shape)
|
||||||
|
// funnel (Funnel Shape)
|
||||||
|
// gear6 (Gear 6 Shape)
|
||||||
|
// gear9 (Gear 9 Shape)
|
||||||
|
// halfFrame (Half Frame Shape)
|
||||||
|
// heart (Heart Shape)
|
||||||
|
// heptagon (Heptagon Shape)
|
||||||
|
// hexagon (Hexagon Shape)
|
||||||
|
// homePlate (Home Plate Shape)
|
||||||
|
// horizontalScroll (Horizontal Scroll Shape)
|
||||||
|
// irregularSeal1 (Irregular Seal 1 Shape)
|
||||||
|
// irregularSeal2 (Irregular Seal 2 Shape)
|
||||||
|
// leftArrow (Left Arrow Shape)
|
||||||
|
// leftArrowCallout (Callout Left Arrow Shape)
|
||||||
|
// leftBrace (Left Brace Shape)
|
||||||
|
// leftBracket (Left Bracket Shape)
|
||||||
|
// leftCircularArrow (Left Circular Arrow Shape)
|
||||||
|
// leftRightArrow (Left Right Arrow Shape)
|
||||||
|
// leftRightArrowCallout (Callout Left Right Arrow Shape)
|
||||||
|
// leftRightCircularArrow (Left Right Circular Arrow Shape)
|
||||||
|
// leftRightRibbon (Left Right Ribbon Shape)
|
||||||
|
// leftRightUpArrow (Left Right Up Arrow Shape)
|
||||||
|
// leftUpArrow (Left Up Arrow Shape)
|
||||||
|
// lightningBolt (Lightning Bolt Shape)
|
||||||
|
// line (Line Shape)
|
||||||
|
// lineInv (Line Inverse Shape)
|
||||||
|
// mathDivide (Divide Math Shape)
|
||||||
|
// mathEqual (Equal Math Shape)
|
||||||
|
// mathMinus (Minus Math Shape)
|
||||||
|
// mathMultiply (Multiply Math Shape)
|
||||||
|
// mathNotEqual (Not Equal Math Shape)
|
||||||
|
// mathPlus (Plus Math Shape)
|
||||||
|
// moon (Moon Shape)
|
||||||
|
// nonIsoscelesTrapezoid (Non-Isosceles Trapezoid Shape)
|
||||||
|
// noSmoking (No Smoking Shape)
|
||||||
|
// notchedRightArrow (Notched Right Arrow Shape)
|
||||||
|
// octagon (Octagon Shape)
|
||||||
|
// parallelogram (Parallelogram Shape)
|
||||||
|
// pentagon (Pentagon Shape)
|
||||||
|
// pie (Pie Shape)
|
||||||
|
// pieWedge (Pie Wedge Shape)
|
||||||
|
// plaque (Plaque Shape)
|
||||||
|
// plaqueTabs (Plaque Tabs Shape)
|
||||||
|
// plus (Plus Shape)
|
||||||
|
// quadArrow (Quad-Arrow Shape)
|
||||||
|
// quadArrowCallout (Callout Quad-Arrow Shape)
|
||||||
|
// rect (Rectangle Shape)
|
||||||
|
// ribbon (Ribbon Shape)
|
||||||
|
// ribbon2 (Ribbon 2 Shape)
|
||||||
|
// rightArrow (Right Arrow Shape)
|
||||||
|
// rightArrowCallout (Callout Right Arrow Shape)
|
||||||
|
// rightBrace (Right Brace Shape)
|
||||||
|
// rightBracket (Right Bracket Shape)
|
||||||
|
// round1Rect (One Round Corner Rectangle Shape)
|
||||||
|
// round2DiagRect (Two Diagonal Round Corner Rectangle Shape)
|
||||||
|
// round2SameRect (Two Same-side Round Corner Rectangle Shape)
|
||||||
|
// roundRect (Round Corner Rectangle Shape)
|
||||||
|
// rtTriangle (Right Triangle Shape)
|
||||||
|
// smileyFace (Smiley Face Shape)
|
||||||
|
// snip1Rect (One Snip Corner Rectangle Shape)
|
||||||
|
// snip2DiagRect (Two Diagonal Snip Corner Rectangle Shape)
|
||||||
|
// snip2SameRect (Two Same-side Snip Corner Rectangle Shape)
|
||||||
|
// snipRoundRect (One Snip One Round Corner Rectangle Shape)
|
||||||
|
// squareTabs (Square Tabs Shape)
|
||||||
|
// star10 (Ten Pointed Star Shape)
|
||||||
|
// star12 (Twelve Pointed Star Shape)
|
||||||
|
// star16 (Sixteen Pointed Star Shape)
|
||||||
|
// star24 (Twenty Four Pointed Star Shape)
|
||||||
|
// star32 (Thirty Two Pointed Star Shape)
|
||||||
|
// star4 (Four Pointed Star Shape)
|
||||||
|
// star5 (Five Pointed Star Shape)
|
||||||
|
// star6 (Six Pointed Star Shape)
|
||||||
|
// star7 (Seven Pointed Star Shape)
|
||||||
|
// star8 (Eight Pointed Star Shape)
|
||||||
|
// straightConnector1 (Straight Connector 1 Shape)
|
||||||
|
// stripedRightArrow (Striped Right Arrow Shape)
|
||||||
|
// sun (Sun Shape)
|
||||||
|
// swooshArrow (Swoosh Arrow Shape)
|
||||||
|
// teardrop (Teardrop Shape)
|
||||||
|
// trapezoid (Trapezoid Shape)
|
||||||
|
// triangle (Triangle Shape)
|
||||||
|
// upArrow (Up Arrow Shape)
|
||||||
|
// upArrowCallout (Callout Up Arrow Shape)
|
||||||
|
// upDownArrow (Up Down Arrow Shape)
|
||||||
|
// upDownArrowCallout (Callout Up Down Arrow Shape)
|
||||||
|
// uturnArrow (U-Turn Arrow Shape)
|
||||||
|
// verticalScroll (Vertical Scroll Shape)
|
||||||
|
// wave (Wave Shape)
|
||||||
|
// wedgeEllipseCallout (Callout Wedge Ellipse Shape)
|
||||||
|
// wedgeRectCallout (Callout Wedge Rectangle Shape)
|
||||||
|
// wedgeRoundRectCallout (Callout Wedge Round Rectangle Shape)
|
||||||
|
//
|
||||||
|
// The following shows the type of text underline supported by excelize:
|
||||||
|
//
|
||||||
|
// none
|
||||||
|
// words
|
||||||
|
// sng
|
||||||
|
// dbl
|
||||||
|
// heavy
|
||||||
|
// dotted
|
||||||
|
// dottedHeavy
|
||||||
|
// dash
|
||||||
|
// dashHeavy
|
||||||
|
// dashLong
|
||||||
|
// dashLongHeavy
|
||||||
|
// dotDash
|
||||||
|
// dotDashHeavy
|
||||||
|
// dotDotDash
|
||||||
|
// dotDotDashHeavy
|
||||||
|
// wavy
|
||||||
|
// wavyHeavy
|
||||||
|
// wavyDbl
|
||||||
|
//
|
||||||
|
func (f *File) AddShape(sheet, cell, format string) error {
|
||||||
|
formatSet, err := parseFormatShapeSet(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Read sheet data.
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
// Add first shape for given sheet, create xl/drawings/ and xl/drawings/_rels/ folder.
|
||||||
|
drawingID := f.countDrawings() + 1
|
||||||
|
drawingXML := "xl/drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
|
||||||
|
sheetRelationshipsDrawingXML := "../drawings/drawing" + strconv.Itoa(drawingID) + ".xml"
|
||||||
|
|
||||||
|
if xlsx.Drawing != nil {
|
||||||
|
// The worksheet already has a shape or chart relationships, use the relationships drawing ../drawings/drawing%d.xml.
|
||||||
|
sheetRelationshipsDrawingXML = f.getSheetRelationshipsTargetByID(sheet, xlsx.Drawing.RID)
|
||||||
|
drawingID, _ = strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(sheetRelationshipsDrawingXML, "../drawings/drawing"), ".xml"))
|
||||||
|
drawingXML = strings.Replace(sheetRelationshipsDrawingXML, "..", "xl", -1)
|
||||||
|
} else {
|
||||||
|
// Add first shape for given sheet.
|
||||||
|
rID := f.addSheetRelationships(sheet, SourceRelationshipDrawingML, sheetRelationshipsDrawingXML, "")
|
||||||
|
f.addSheetDrawing(sheet, rID)
|
||||||
|
}
|
||||||
|
f.addDrawingShape(sheet, drawingXML, cell, formatSet)
|
||||||
|
f.addContentTypePart(drawingID, "drawings")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDrawingShape provides a function to add preset geometry by given sheet,
|
||||||
|
// drawingXMLand format sets.
|
||||||
|
func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *formatShape) {
|
||||||
|
textUnderlineType := map[string]bool{"none": true, "words": true, "sng": true, "dbl": true, "heavy": true, "dotted": true, "dottedHeavy": true, "dash": true, "dashHeavy": true, "dashLong": true, "dashLongHeavy": true, "dotDash": true, "dotDashHeavy": true, "dotDotDash": true, "dotDotDashHeavy": true, "wavy": true, "wavyHeavy": true, "wavyDbl": true}
|
||||||
|
cell = strings.ToUpper(cell)
|
||||||
|
fromCol := string(strings.Map(letterOnlyMapF, cell))
|
||||||
|
fromRow, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
|
||||||
|
row := fromRow - 1
|
||||||
|
col := TitleToNumber(fromCol)
|
||||||
|
width := int(float64(formatSet.Width) * formatSet.Format.XScale)
|
||||||
|
height := int(float64(formatSet.Height) * formatSet.Format.YScale)
|
||||||
|
colStart, rowStart, _, _, colEnd, rowEnd, x2, y2 := f.positionObjectPixels(sheet, col, row, formatSet.Format.OffsetX, formatSet.Format.OffsetY, width, height)
|
||||||
|
content := xlsxWsDr{}
|
||||||
|
content.A = NameSpaceDrawingML
|
||||||
|
content.Xdr = NameSpaceDrawingMLSpreadSheet
|
||||||
|
cNvPrID := f.drawingParser(drawingXML, &content)
|
||||||
|
twoCellAnchor := xdrCellAnchor{}
|
||||||
|
twoCellAnchor.EditAs = formatSet.Format.Positioning
|
||||||
|
from := xlsxFrom{}
|
||||||
|
from.Col = colStart
|
||||||
|
from.ColOff = formatSet.Format.OffsetX * EMU
|
||||||
|
from.Row = rowStart
|
||||||
|
from.RowOff = formatSet.Format.OffsetY * EMU
|
||||||
|
to := xlsxTo{}
|
||||||
|
to.Col = colEnd
|
||||||
|
to.ColOff = x2 * EMU
|
||||||
|
to.Row = rowEnd
|
||||||
|
to.RowOff = y2 * EMU
|
||||||
|
twoCellAnchor.From = &from
|
||||||
|
twoCellAnchor.To = &to
|
||||||
|
shape := xdrSp{
|
||||||
|
NvSpPr: &xdrNvSpPr{
|
||||||
|
CNvPr: &xlsxCNvPr{
|
||||||
|
ID: cNvPrID,
|
||||||
|
Name: "Shape " + strconv.Itoa(cNvPrID),
|
||||||
|
},
|
||||||
|
CNvSpPr: &xdrCNvSpPr{
|
||||||
|
TxBox: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SpPr: &xlsxSpPr{
|
||||||
|
PrstGeom: xlsxPrstGeom{
|
||||||
|
Prst: formatSet.Type,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Style: &xdrStyle{
|
||||||
|
LnRef: setShapeRef(formatSet.Color.Line, 2),
|
||||||
|
FillRef: setShapeRef(formatSet.Color.Fill, 1),
|
||||||
|
EffectRef: setShapeRef(formatSet.Color.Effect, 0),
|
||||||
|
FontRef: &aFontRef{
|
||||||
|
Idx: "minor",
|
||||||
|
SchemeClr: &attrValString{
|
||||||
|
Val: "tx1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TxBody: &xdrTxBody{
|
||||||
|
BodyPr: &aBodyPr{
|
||||||
|
VertOverflow: "clip",
|
||||||
|
HorzOverflow: "clip",
|
||||||
|
Wrap: "none",
|
||||||
|
RtlCol: false,
|
||||||
|
Anchor: "t",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if len(formatSet.Paragraph) < 1 {
|
||||||
|
formatSet.Paragraph = []formatShapeParagraph{
|
||||||
|
{
|
||||||
|
Font: formatFont{
|
||||||
|
Bold: false,
|
||||||
|
Italic: false,
|
||||||
|
Underline: "none",
|
||||||
|
Family: "Calibri",
|
||||||
|
Size: 11,
|
||||||
|
Color: "#000000",
|
||||||
|
},
|
||||||
|
Text: " ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, p := range formatSet.Paragraph {
|
||||||
|
u := p.Font.Underline
|
||||||
|
_, ok := textUnderlineType[u]
|
||||||
|
if !ok {
|
||||||
|
u = "none"
|
||||||
|
}
|
||||||
|
text := p.Text
|
||||||
|
if text == "" {
|
||||||
|
text = " "
|
||||||
|
}
|
||||||
|
paragraph := &aP{
|
||||||
|
R: &aR{
|
||||||
|
RPr: aRPr{
|
||||||
|
I: p.Font.Italic,
|
||||||
|
B: p.Font.Bold,
|
||||||
|
Lang: "en-US",
|
||||||
|
AltLang: "en-US",
|
||||||
|
U: u,
|
||||||
|
Sz: p.Font.Size * 100,
|
||||||
|
Latin: &aLatin{Typeface: p.Font.Family},
|
||||||
|
SolidFill: &aSolidFill{
|
||||||
|
SrgbClr: &attrValString{
|
||||||
|
Val: strings.Replace(strings.ToUpper(p.Font.Color), "#", "", -1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
T: text,
|
||||||
|
},
|
||||||
|
EndParaRPr: &aEndParaRPr{
|
||||||
|
Lang: "en-US",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
shape.TxBody.P = append(shape.TxBody.P, paragraph)
|
||||||
|
}
|
||||||
|
twoCellAnchor.Sp = &shape
|
||||||
|
twoCellAnchor.ClientData = &xdrClientData{
|
||||||
|
FLocksWithSheet: formatSet.Format.FLocksWithSheet,
|
||||||
|
FPrintsWithSheet: formatSet.Format.FPrintsWithSheet,
|
||||||
|
}
|
||||||
|
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
|
||||||
|
output, _ := xml.Marshal(content)
|
||||||
|
f.saveFileList(drawingXML, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setShapeRef provides a function to set color with hex model by given actual
|
||||||
|
// color value.
|
||||||
|
func setShapeRef(color string, i int) *aRef {
|
||||||
|
if color == "" {
|
||||||
|
return &aRef{
|
||||||
|
Idx: 0,
|
||||||
|
ScrgbClr: &aScrgbClr{
|
||||||
|
R: 0,
|
||||||
|
G: 0,
|
||||||
|
B: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &aRef{
|
||||||
|
Idx: i,
|
||||||
|
SrgbClr: &attrValString{
|
||||||
|
Val: strings.Replace(strings.ToUpper(color), "#", "", -1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
792
vendor/github.com/360EntSecGroup-Skylar/excelize/sheet.go
generated
vendored
Normal file
792
vendor/github.com/360EntSecGroup-Skylar/excelize/sheet.go
generated
vendored
Normal file
@ -0,0 +1,792 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/mohae/deepcopy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSheet provides function to create a new sheet by given worksheet name.
|
||||||
|
// When creating a new XLSX file, the default sheet will be created. Returns
|
||||||
|
// the number of sheets in the workbook (file) after appending the new sheet.
|
||||||
|
func (f *File) NewSheet(name string) int {
|
||||||
|
// Check if the worksheet already exists
|
||||||
|
if f.GetSheetIndex(name) != 0 {
|
||||||
|
return f.SheetCount
|
||||||
|
}
|
||||||
|
f.DeleteSheet(name)
|
||||||
|
f.SheetCount++
|
||||||
|
wb := f.workbookReader()
|
||||||
|
sheetID := 0
|
||||||
|
for _, v := range wb.Sheets.Sheet {
|
||||||
|
if v.SheetID > sheetID {
|
||||||
|
sheetID = v.SheetID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sheetID++
|
||||||
|
// Update docProps/app.xml
|
||||||
|
f.setAppXML()
|
||||||
|
// Update [Content_Types].xml
|
||||||
|
f.setContentTypes(sheetID)
|
||||||
|
// Create new sheet /xl/worksheets/sheet%d.xml
|
||||||
|
f.setSheet(sheetID, name)
|
||||||
|
// Update xl/_rels/workbook.xml.rels
|
||||||
|
rID := f.addXlsxWorkbookRels(sheetID)
|
||||||
|
// Update xl/workbook.xml
|
||||||
|
f.setWorkbook(name, sheetID, rID)
|
||||||
|
return sheetID
|
||||||
|
}
|
||||||
|
|
||||||
|
// contentTypesReader provides a function to get the pointer to the
|
||||||
|
// [Content_Types].xml structure after deserialization.
|
||||||
|
func (f *File) contentTypesReader() *xlsxTypes {
|
||||||
|
if f.ContentTypes == nil {
|
||||||
|
var content xlsxTypes
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("[Content_Types].xml")), &content)
|
||||||
|
f.ContentTypes = &content
|
||||||
|
}
|
||||||
|
return f.ContentTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
// contentTypesWriter provides a function to save [Content_Types].xml after
|
||||||
|
// serialize structure.
|
||||||
|
func (f *File) contentTypesWriter() {
|
||||||
|
if f.ContentTypes != nil {
|
||||||
|
output, _ := xml.Marshal(f.ContentTypes)
|
||||||
|
f.saveFileList("[Content_Types].xml", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// workbookReader provides a function to get the pointer to the xl/workbook.xml
|
||||||
|
// structure after deserialization.
|
||||||
|
func (f *File) workbookReader() *xlsxWorkbook {
|
||||||
|
if f.WorkBook == nil {
|
||||||
|
var content xlsxWorkbook
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/workbook.xml")), &content)
|
||||||
|
f.WorkBook = &content
|
||||||
|
}
|
||||||
|
return f.WorkBook
|
||||||
|
}
|
||||||
|
|
||||||
|
// workbookWriter provides a function to save xl/workbook.xml after serialize
|
||||||
|
// structure.
|
||||||
|
func (f *File) workbookWriter() {
|
||||||
|
if f.WorkBook != nil {
|
||||||
|
output, _ := xml.Marshal(f.WorkBook)
|
||||||
|
f.saveFileList("xl/workbook.xml", replaceRelationshipsNameSpaceBytes(output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// worksheetWriter provides a function to save xl/worksheets/sheet%d.xml after
|
||||||
|
// serialize structure.
|
||||||
|
func (f *File) worksheetWriter() {
|
||||||
|
for path, sheet := range f.Sheet {
|
||||||
|
if sheet != nil {
|
||||||
|
for k, v := range sheet.SheetData.Row {
|
||||||
|
f.Sheet[path].SheetData.Row[k].C = trimCell(v.C)
|
||||||
|
}
|
||||||
|
output, _ := xml.Marshal(sheet)
|
||||||
|
f.saveFileList(path, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
|
||||||
|
ok := f.checked[path]
|
||||||
|
if ok {
|
||||||
|
f.checked[path] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimCell provides a function to trim blank cells which created by completeCol.
|
||||||
|
func trimCell(column []xlsxC) []xlsxC {
|
||||||
|
col := make([]xlsxC, len(column))
|
||||||
|
i := 0
|
||||||
|
for _, c := range column {
|
||||||
|
if c.S != 0 || c.V != "" || c.F != nil || c.T != "" {
|
||||||
|
col[i] = c
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return col[0:i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// setContentTypes provides a function to read and update property of contents
|
||||||
|
// type of XLSX.
|
||||||
|
func (f *File) setContentTypes(index int) {
|
||||||
|
content := f.contentTypesReader()
|
||||||
|
content.Overrides = append(content.Overrides, xlsxOverride{
|
||||||
|
PartName: "/xl/worksheets/sheet" + strconv.Itoa(index) + ".xml",
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// setSheet provides a function to update sheet property by given index.
|
||||||
|
func (f *File) setSheet(index int, name string) {
|
||||||
|
var xlsx xlsxWorksheet
|
||||||
|
xlsx.Dimension.Ref = "A1"
|
||||||
|
xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{
|
||||||
|
WorkbookViewID: 0,
|
||||||
|
})
|
||||||
|
path := "xl/worksheets/sheet" + strconv.Itoa(index) + ".xml"
|
||||||
|
f.sheetMap[trimSheetName(name)] = path
|
||||||
|
f.Sheet[path] = &xlsx
|
||||||
|
}
|
||||||
|
|
||||||
|
// setWorkbook update workbook property of XLSX. Maximum 31 characters are
|
||||||
|
// allowed in sheet title.
|
||||||
|
func (f *File) setWorkbook(name string, sheetID, rid int) {
|
||||||
|
content := f.workbookReader()
|
||||||
|
content.Sheets.Sheet = append(content.Sheets.Sheet, xlsxSheet{
|
||||||
|
Name: trimSheetName(name),
|
||||||
|
SheetID: sheetID,
|
||||||
|
ID: "rId" + strconv.Itoa(rid),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// workbookRelsReader provides a function to read and unmarshal workbook
|
||||||
|
// relationships of XLSX file.
|
||||||
|
func (f *File) workbookRelsReader() *xlsxWorkbookRels {
|
||||||
|
if f.WorkBookRels == nil {
|
||||||
|
var content xlsxWorkbookRels
|
||||||
|
_ = xml.Unmarshal(namespaceStrictToTransitional(f.readXML("xl/_rels/workbook.xml.rels")), &content)
|
||||||
|
f.WorkBookRels = &content
|
||||||
|
}
|
||||||
|
return f.WorkBookRels
|
||||||
|
}
|
||||||
|
|
||||||
|
// workbookRelsWriter provides a function to save xl/_rels/workbook.xml.rels after
|
||||||
|
// serialize structure.
|
||||||
|
func (f *File) workbookRelsWriter() {
|
||||||
|
if f.WorkBookRels != nil {
|
||||||
|
output, _ := xml.Marshal(f.WorkBookRels)
|
||||||
|
f.saveFileList("xl/_rels/workbook.xml.rels", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addXlsxWorkbookRels update workbook relationships property of XLSX.
|
||||||
|
func (f *File) addXlsxWorkbookRels(sheet int) int {
|
||||||
|
content := f.workbookRelsReader()
|
||||||
|
rID := 0
|
||||||
|
for _, v := range content.Relationships {
|
||||||
|
t, _ := strconv.Atoi(strings.TrimPrefix(v.ID, "rId"))
|
||||||
|
if t > rID {
|
||||||
|
rID = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rID++
|
||||||
|
ID := bytes.Buffer{}
|
||||||
|
ID.WriteString("rId")
|
||||||
|
ID.WriteString(strconv.Itoa(rID))
|
||||||
|
target := bytes.Buffer{}
|
||||||
|
target.WriteString("worksheets/sheet")
|
||||||
|
target.WriteString(strconv.Itoa(sheet))
|
||||||
|
target.WriteString(".xml")
|
||||||
|
content.Relationships = append(content.Relationships, xlsxWorkbookRelation{
|
||||||
|
ID: ID.String(),
|
||||||
|
Target: target.String(),
|
||||||
|
Type: SourceRelationshipWorkSheet,
|
||||||
|
})
|
||||||
|
return rID
|
||||||
|
}
|
||||||
|
|
||||||
|
// setAppXML update docProps/app.xml file of XML.
|
||||||
|
func (f *File) setAppXML() {
|
||||||
|
f.saveFileList("docProps/app.xml", []byte(templateDocpropsApp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceRelationshipsNameSpaceBytes; Some tools that read XLSX files have
|
||||||
|
// very strict requirements about the structure of the input XML. In
|
||||||
|
// particular both Numbers on the Mac and SAS dislike inline XML namespace
|
||||||
|
// declarations, or namespace prefixes that don't match the ones that Excel
|
||||||
|
// itself uses. This is a problem because the Go XML library doesn't multiple
|
||||||
|
// namespace declarations in a single element of a document. This function is
|
||||||
|
// a horrible hack to fix that after the XML marshalling is completed.
|
||||||
|
func replaceRelationshipsNameSpaceBytes(workbookMarshal []byte) []byte {
|
||||||
|
oldXmlns := []byte(`<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">`)
|
||||||
|
newXmlns := []byte(`<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x15" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main">`)
|
||||||
|
return bytes.Replace(workbookMarshal, oldXmlns, newXmlns, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetActiveSheet provides function to set default active worksheet of XLSX by
|
||||||
|
// given index. Note that active index is different from the index returned by
|
||||||
|
// function GetSheetMap(). It should be greater than 0 and less than total
|
||||||
|
// worksheet numbers.
|
||||||
|
func (f *File) SetActiveSheet(index int) {
|
||||||
|
if index < 1 {
|
||||||
|
index = 1
|
||||||
|
}
|
||||||
|
wb := f.workbookReader()
|
||||||
|
for activeTab, sheet := range wb.Sheets.Sheet {
|
||||||
|
if sheet.SheetID == index {
|
||||||
|
if len(wb.BookViews.WorkBookView) > 0 {
|
||||||
|
wb.BookViews.WorkBookView[0].ActiveTab = activeTab
|
||||||
|
} else {
|
||||||
|
wb.BookViews.WorkBookView = append(wb.BookViews.WorkBookView, xlsxWorkBookView{
|
||||||
|
ActiveTab: activeTab,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for idx, name := range f.GetSheetMap() {
|
||||||
|
xlsx := f.workSheetReader(name)
|
||||||
|
if len(xlsx.SheetViews.SheetView) > 0 {
|
||||||
|
xlsx.SheetViews.SheetView[0].TabSelected = false
|
||||||
|
}
|
||||||
|
if index == idx {
|
||||||
|
if len(xlsx.SheetViews.SheetView) > 0 {
|
||||||
|
xlsx.SheetViews.SheetView[0].TabSelected = true
|
||||||
|
} else {
|
||||||
|
xlsx.SheetViews.SheetView = append(xlsx.SheetViews.SheetView, xlsxSheetView{
|
||||||
|
TabSelected: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActiveSheetIndex provides a function to get active sheet index of the
|
||||||
|
// XLSX. If not found the active sheet will be return integer 0.
|
||||||
|
func (f *File) GetActiveSheetIndex() int {
|
||||||
|
for idx, name := range f.GetSheetMap() {
|
||||||
|
xlsx := f.workSheetReader(name)
|
||||||
|
for _, sheetView := range xlsx.SheetViews.SheetView {
|
||||||
|
if sheetView.TabSelected {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetName provides a function to set the worksheet name be given old and new
|
||||||
|
// worksheet name. Maximum 31 characters are allowed in sheet title and this
|
||||||
|
// function only changes the name of the sheet and will not update the sheet
|
||||||
|
// name in the formula or reference associated with the cell. So there may be
|
||||||
|
// problem formula error or reference missing.
|
||||||
|
func (f *File) SetSheetName(oldName, newName string) {
|
||||||
|
oldName = trimSheetName(oldName)
|
||||||
|
newName = trimSheetName(newName)
|
||||||
|
content := f.workbookReader()
|
||||||
|
for k, v := range content.Sheets.Sheet {
|
||||||
|
if v.Name == oldName {
|
||||||
|
content.Sheets.Sheet[k].Name = newName
|
||||||
|
f.sheetMap[newName] = f.sheetMap[oldName]
|
||||||
|
delete(f.sheetMap, oldName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetName provides a function to get worksheet name of XLSX by given
|
||||||
|
// worksheet index. If given sheet index is invalid, will return an empty
|
||||||
|
// string.
|
||||||
|
func (f *File) GetSheetName(index int) string {
|
||||||
|
content := f.workbookReader()
|
||||||
|
rels := f.workbookRelsReader()
|
||||||
|
for _, rel := range rels.Relationships {
|
||||||
|
rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml"))
|
||||||
|
if rID == index {
|
||||||
|
for _, v := range content.Sheets.Sheet {
|
||||||
|
if v.ID == rel.ID {
|
||||||
|
return v.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetIndex provides a function to get worksheet index of XLSX by given sheet
|
||||||
|
// name. If given worksheet name is invalid, will return an integer type value
|
||||||
|
// 0.
|
||||||
|
func (f *File) GetSheetIndex(name string) int {
|
||||||
|
content := f.workbookReader()
|
||||||
|
rels := f.workbookRelsReader()
|
||||||
|
for _, v := range content.Sheets.Sheet {
|
||||||
|
if v.Name == name {
|
||||||
|
for _, rel := range rels.Relationships {
|
||||||
|
if v.ID == rel.ID {
|
||||||
|
rID, _ := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(rel.Target, "worksheets/sheet"), ".xml"))
|
||||||
|
return rID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetMap provides a function to get worksheet name and index map of XLSX.
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// xlsx, err := excelize.OpenFile("./Book1.xlsx")
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// for index, name := range xlsx.GetSheetMap() {
|
||||||
|
// fmt.Println(index, name)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (f *File) GetSheetMap() map[int]string {
|
||||||
|
content := f.workbookReader()
|
||||||
|
rels := f.workbookRelsReader()
|
||||||
|
sheetMap := map[int]string{}
|
||||||
|
for _, v := range content.Sheets.Sheet {
|
||||||
|
for _, rel := range rels.Relationships {
|
||||||
|
relStr := strings.SplitN(rel.Target, "worksheets/sheet", 2)
|
||||||
|
if rel.ID == v.ID && len(relStr) == 2 {
|
||||||
|
rID, _ := strconv.Atoi(strings.TrimSuffix(relStr[1], ".xml"))
|
||||||
|
sheetMap[rID] = v.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sheetMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSheetMap provides a function to get worksheet name and XML file path map of
|
||||||
|
// XLSX.
|
||||||
|
func (f *File) getSheetMap() map[string]string {
|
||||||
|
maps := make(map[string]string)
|
||||||
|
for idx, name := range f.GetSheetMap() {
|
||||||
|
maps[name] = "xl/worksheets/sheet" + strconv.Itoa(idx) + ".xml"
|
||||||
|
}
|
||||||
|
return maps
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetBackground provides a function to set background picture by given
|
||||||
|
// worksheet name and file path.
|
||||||
|
func (f *File) SetSheetBackground(sheet, picture string) error {
|
||||||
|
var err error
|
||||||
|
// Check picture exists first.
|
||||||
|
if _, err = os.Stat(picture); os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ext, ok := supportImageTypes[path.Ext(picture)]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("unsupported image extension")
|
||||||
|
}
|
||||||
|
pictureID := f.countMedia() + 1
|
||||||
|
rID := f.addSheetRelationships(sheet, SourceRelationshipImage, "../media/image"+strconv.Itoa(pictureID)+ext, "")
|
||||||
|
f.addSheetPicture(sheet, rID)
|
||||||
|
file, _ := ioutil.ReadFile(picture)
|
||||||
|
f.addMedia(file, ext)
|
||||||
|
f.setContentTypePartImageExtensions()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSheet provides a function to delete worksheet in a workbook by given
|
||||||
|
// worksheet name. Use this method with caution, which will affect changes in
|
||||||
|
// references such as formulas, charts, and so on. If there is any referenced
|
||||||
|
// value of the deleted worksheet, it will cause a file error when you open it.
|
||||||
|
// This function will be invalid when only the one worksheet is left.
|
||||||
|
func (f *File) DeleteSheet(name string) {
|
||||||
|
content := f.workbookReader()
|
||||||
|
for k, v := range content.Sheets.Sheet {
|
||||||
|
if v.Name == trimSheetName(name) && len(content.Sheets.Sheet) > 1 {
|
||||||
|
content.Sheets.Sheet = append(content.Sheets.Sheet[:k], content.Sheets.Sheet[k+1:]...)
|
||||||
|
sheet := "xl/worksheets/sheet" + strconv.Itoa(v.SheetID) + ".xml"
|
||||||
|
rels := "xl/worksheets/_rels/sheet" + strconv.Itoa(v.SheetID) + ".xml.rels"
|
||||||
|
target := f.deleteSheetFromWorkbookRels(v.ID)
|
||||||
|
f.deleteSheetFromContentTypes(target)
|
||||||
|
delete(f.sheetMap, name)
|
||||||
|
delete(f.XLSX, sheet)
|
||||||
|
delete(f.XLSX, rels)
|
||||||
|
delete(f.Sheet, sheet)
|
||||||
|
f.SheetCount--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.SetActiveSheet(len(f.GetSheetMap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSheetFromWorkbookRels provides a function to remove worksheet
|
||||||
|
// relationships by given relationships ID in the file
|
||||||
|
// xl/_rels/workbook.xml.rels.
|
||||||
|
func (f *File) deleteSheetFromWorkbookRels(rID string) string {
|
||||||
|
content := f.workbookRelsReader()
|
||||||
|
for k, v := range content.Relationships {
|
||||||
|
if v.ID == rID {
|
||||||
|
content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...)
|
||||||
|
return v.Target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteSheetFromContentTypes provides a function to remove worksheet
|
||||||
|
// relationships by given target name in the file [Content_Types].xml.
|
||||||
|
func (f *File) deleteSheetFromContentTypes(target string) {
|
||||||
|
content := f.contentTypesReader()
|
||||||
|
for k, v := range content.Overrides {
|
||||||
|
if v.PartName == "/xl/"+target {
|
||||||
|
content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopySheet provides a function to duplicate a worksheet by gave source and
|
||||||
|
// target worksheet index. Note that currently doesn't support duplicate
|
||||||
|
// workbooks that contain tables, charts or pictures. For Example:
|
||||||
|
//
|
||||||
|
// // Sheet1 already exists...
|
||||||
|
// index := xlsx.NewSheet("Sheet2")
|
||||||
|
// err := xlsx.CopySheet(1, index)
|
||||||
|
// return err
|
||||||
|
//
|
||||||
|
func (f *File) CopySheet(from, to int) error {
|
||||||
|
if from < 1 || to < 1 || from == to || f.GetSheetName(from) == "" || f.GetSheetName(to) == "" {
|
||||||
|
return errors.New("invalid worksheet index")
|
||||||
|
}
|
||||||
|
f.copySheet(from, to)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copySheet provides a function to duplicate a worksheet by gave source and
|
||||||
|
// target worksheet name.
|
||||||
|
func (f *File) copySheet(from, to int) {
|
||||||
|
sheet := f.workSheetReader("sheet" + strconv.Itoa(from))
|
||||||
|
worksheet := deepcopy.Copy(sheet).(*xlsxWorksheet)
|
||||||
|
path := "xl/worksheets/sheet" + strconv.Itoa(to) + ".xml"
|
||||||
|
if len(worksheet.SheetViews.SheetView) > 0 {
|
||||||
|
worksheet.SheetViews.SheetView[0].TabSelected = false
|
||||||
|
}
|
||||||
|
worksheet.Drawing = nil
|
||||||
|
worksheet.TableParts = nil
|
||||||
|
worksheet.PageSetUp = nil
|
||||||
|
f.Sheet[path] = worksheet
|
||||||
|
toRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(to) + ".xml.rels"
|
||||||
|
fromRels := "xl/worksheets/_rels/sheet" + strconv.Itoa(from) + ".xml.rels"
|
||||||
|
_, ok := f.XLSX[fromRels]
|
||||||
|
if ok {
|
||||||
|
f.XLSX[toRels] = f.XLSX[fromRels]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetVisible provides a function to set worksheet visible by given worksheet
|
||||||
|
// name. A workbook must contain at least one visible worksheet. If the given
|
||||||
|
// worksheet has been activated, this setting will be invalidated. Sheet state
|
||||||
|
// values as defined by http://msdn.microsoft.com/en-us/library/office/documentformat.openxml.spreadsheet.sheetstatevalues.aspx
|
||||||
|
//
|
||||||
|
// visible
|
||||||
|
// hidden
|
||||||
|
// veryHidden
|
||||||
|
//
|
||||||
|
// For example, hide Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetSheetVisible("Sheet1", false)
|
||||||
|
//
|
||||||
|
func (f *File) SetSheetVisible(name string, visible bool) {
|
||||||
|
name = trimSheetName(name)
|
||||||
|
content := f.workbookReader()
|
||||||
|
if visible {
|
||||||
|
for k, v := range content.Sheets.Sheet {
|
||||||
|
if v.Name == name {
|
||||||
|
content.Sheets.Sheet[k].State = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for _, v := range content.Sheets.Sheet {
|
||||||
|
if v.State != "hidden" {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range content.Sheets.Sheet {
|
||||||
|
xlsx := f.workSheetReader(f.GetSheetMap()[k])
|
||||||
|
tabSelected := false
|
||||||
|
if len(xlsx.SheetViews.SheetView) > 0 {
|
||||||
|
tabSelected = xlsx.SheetViews.SheetView[0].TabSelected
|
||||||
|
}
|
||||||
|
if v.Name == name && count > 1 && !tabSelected {
|
||||||
|
content.Sheets.Sheet[k].State = "hidden"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFormatPanesSet provides a function to parse the panes settings.
|
||||||
|
func parseFormatPanesSet(formatSet string) (*formatPanes, error) {
|
||||||
|
format := formatPanes{}
|
||||||
|
err := json.Unmarshal([]byte(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPanes provides a function to create and remove freeze panes and split panes
|
||||||
|
// by given worksheet name and panes format set.
|
||||||
|
//
|
||||||
|
// activePane defines the pane that is active. The possible values for this
|
||||||
|
// attribute are defined in the following table:
|
||||||
|
//
|
||||||
|
// Enumeration Value | Description
|
||||||
|
// --------------------------------+-------------------------------------------------------------
|
||||||
|
// bottomLeft (Bottom Left Pane) | Bottom left pane, when both vertical and horizontal
|
||||||
|
// | splits are applied.
|
||||||
|
// |
|
||||||
|
// | This value is also used when only a horizontal split has
|
||||||
|
// | been applied, dividing the pane into upper and lower
|
||||||
|
// | regions. In that case, this value specifies the bottom
|
||||||
|
// | pane.
|
||||||
|
// |
|
||||||
|
// bottomRight (Bottom Right Pane) | Bottom right pane, when both vertical and horizontal
|
||||||
|
// | splits are applied.
|
||||||
|
// |
|
||||||
|
// topLeft (Top Left Pane) | Top left pane, when both vertical and horizontal splits
|
||||||
|
// | are applied.
|
||||||
|
// |
|
||||||
|
// | This value is also used when only a horizontal split has
|
||||||
|
// | been applied, dividing the pane into upper and lower
|
||||||
|
// | regions. In that case, this value specifies the top pane.
|
||||||
|
// |
|
||||||
|
// | This value is also used when only a vertical split has
|
||||||
|
// | been applied, dividing the pane into right and left
|
||||||
|
// | regions. In that case, this value specifies the left pane
|
||||||
|
// |
|
||||||
|
// topRight (Top Right Pane) | Top right pane, when both vertical and horizontal
|
||||||
|
// | splits are applied.
|
||||||
|
// |
|
||||||
|
// | This value is also used when only a vertical split has
|
||||||
|
// | been applied, dividing the pane into right and left
|
||||||
|
// | regions. In that case, this value specifies the right
|
||||||
|
// | pane.
|
||||||
|
//
|
||||||
|
// Pane state type is restricted to the values supported currently listed in the following table:
|
||||||
|
//
|
||||||
|
// Enumeration Value | Description
|
||||||
|
// --------------------------------+-------------------------------------------------------------
|
||||||
|
// frozen (Frozen) | Panes are frozen, but were not split being frozen. In
|
||||||
|
// | this state, when the panes are unfrozen again, a single
|
||||||
|
// | pane results, with no split.
|
||||||
|
// |
|
||||||
|
// | In this state, the split bars are not adjustable.
|
||||||
|
// |
|
||||||
|
// split (Split) | Panes are split, but not frozen. In this state, the split
|
||||||
|
// | bars are adjustable by the user.
|
||||||
|
//
|
||||||
|
// x_split (Horizontal Split Position): Horizontal position of the split, in
|
||||||
|
// 1/20th of a point; 0 (zero) if none. If the pane is frozen, this value
|
||||||
|
// indicates the number of columns visible in the top pane.
|
||||||
|
//
|
||||||
|
// y_split (Vertical Split Position): Vertical position of the split, in 1/20th
|
||||||
|
// of a point; 0 (zero) if none. If the pane is frozen, this value indicates the
|
||||||
|
// number of rows visible in the left pane. The possible values for this
|
||||||
|
// attribute are defined by the W3C XML Schema double datatype.
|
||||||
|
//
|
||||||
|
// top_left_cell: Location of the top left visible cell in the bottom right pane
|
||||||
|
// (when in Left-To-Right mode).
|
||||||
|
//
|
||||||
|
// sqref (Sequence of References): Range of the selection. Can be non-contiguous
|
||||||
|
// set of ranges.
|
||||||
|
//
|
||||||
|
// An example of how to freeze column A in the Sheet1 and set the active cell on
|
||||||
|
// Sheet1!K16:
|
||||||
|
//
|
||||||
|
// xlsx.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":1,"y_split":0,"top_left_cell":"B1","active_pane":"topRight","panes":[{"sqref":"K16","active_cell":"K16","pane":"topRight"}]}`)
|
||||||
|
//
|
||||||
|
// An example of how to freeze rows 1 to 9 in the Sheet1 and set the active cell
|
||||||
|
// ranges on Sheet1!A11:XFD11:
|
||||||
|
//
|
||||||
|
// xlsx.SetPanes("Sheet1", `{"freeze":true,"split":false,"x_split":0,"y_split":9,"top_left_cell":"A34","active_pane":"bottomLeft","panes":[{"sqref":"A11:XFD11","active_cell":"A11","pane":"bottomLeft"}]}`)
|
||||||
|
//
|
||||||
|
// An example of how to create split panes in the Sheet1 and set the active cell
|
||||||
|
// on Sheet1!J60:
|
||||||
|
//
|
||||||
|
// xlsx.SetPanes("Sheet1", `{"freeze":false,"split":true,"x_split":3270,"y_split":1800,"top_left_cell":"N57","active_pane":"bottomLeft","panes":[{"sqref":"I36","active_cell":"I36"},{"sqref":"G33","active_cell":"G33","pane":"topRight"},{"sqref":"J60","active_cell":"J60","pane":"bottomLeft"},{"sqref":"O60","active_cell":"O60","pane":"bottomRight"}]}`)
|
||||||
|
//
|
||||||
|
// An example of how to unfreeze and remove all panes on Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SetPanes("Sheet1", `{"freeze":false,"split":false}`)
|
||||||
|
//
|
||||||
|
func (f *File) SetPanes(sheet, panes string) {
|
||||||
|
fs, _ := parseFormatPanesSet(panes)
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
p := &xlsxPane{
|
||||||
|
ActivePane: fs.ActivePane,
|
||||||
|
TopLeftCell: fs.TopLeftCell,
|
||||||
|
XSplit: float64(fs.XSplit),
|
||||||
|
YSplit: float64(fs.YSplit),
|
||||||
|
}
|
||||||
|
if fs.Freeze {
|
||||||
|
p.State = "frozen"
|
||||||
|
}
|
||||||
|
xlsx.SheetViews.SheetView[len(xlsx.SheetViews.SheetView)-1].Pane = p
|
||||||
|
if !(fs.Freeze) && !(fs.Split) {
|
||||||
|
if len(xlsx.SheetViews.SheetView) > 0 {
|
||||||
|
xlsx.SheetViews.SheetView[len(xlsx.SheetViews.SheetView)-1].Pane = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s := []*xlsxSelection{}
|
||||||
|
for _, p := range fs.Panes {
|
||||||
|
s = append(s, &xlsxSelection{
|
||||||
|
ActiveCell: p.ActiveCell,
|
||||||
|
Pane: p.Pane,
|
||||||
|
SQRef: p.SQRef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
xlsx.SheetViews.SheetView[len(xlsx.SheetViews.SheetView)-1].Selection = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetVisible provides a function to get worksheet visible by given worksheet
|
||||||
|
// name. For example, get visible state of Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.GetSheetVisible("Sheet1")
|
||||||
|
//
|
||||||
|
func (f *File) GetSheetVisible(name string) bool {
|
||||||
|
content := f.workbookReader()
|
||||||
|
visible := false
|
||||||
|
for k, v := range content.Sheets.Sheet {
|
||||||
|
if v.Name == trimSheetName(name) {
|
||||||
|
if content.Sheets.Sheet[k].State == "" || content.Sheets.Sheet[k].State == "visible" {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visible
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchSheet provides a function to get coordinates by given worksheet name,
|
||||||
|
// cell value, and regular expression. The function doesn't support searching
|
||||||
|
// on the calculated result, formatted numbers and conditional lookup
|
||||||
|
// currently. If it is a merged cell, it will return the coordinates of the
|
||||||
|
// upper left corner of the merged area.
|
||||||
|
//
|
||||||
|
// An example of search the coordinates of the value of "100" on Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.SearchSheet("Sheet1", "100")
|
||||||
|
//
|
||||||
|
// An example of search the coordinates where the numerical value in the range
|
||||||
|
// of "0-9" of Sheet1 is described:
|
||||||
|
//
|
||||||
|
// xlsx.SearchSheet("Sheet1", "[0-9]", true)
|
||||||
|
//
|
||||||
|
func (f *File) SearchSheet(sheet, value string, reg ...bool) []string {
|
||||||
|
var regSearch bool
|
||||||
|
for _, r := range reg {
|
||||||
|
regSearch = r
|
||||||
|
}
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
result := []string{}
|
||||||
|
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||||
|
if !ok {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if xlsx != nil {
|
||||||
|
output, _ := xml.Marshal(f.Sheet[name])
|
||||||
|
f.saveFileList(name, replaceWorkSheetsRelationshipsNameSpaceBytes(output))
|
||||||
|
}
|
||||||
|
xml.NewDecoder(bytes.NewReader(f.readXML(name)))
|
||||||
|
d := f.sharedStringsReader()
|
||||||
|
var inElement string
|
||||||
|
var r xlsxRow
|
||||||
|
decoder := xml.NewDecoder(bytes.NewReader(f.readXML(name)))
|
||||||
|
for {
|
||||||
|
token, _ := decoder.Token()
|
||||||
|
if token == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch startElement := token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
inElement = startElement.Name.Local
|
||||||
|
if inElement == "row" {
|
||||||
|
r = xlsxRow{}
|
||||||
|
_ = decoder.DecodeElement(&r, &startElement)
|
||||||
|
for _, colCell := range r.C {
|
||||||
|
val, _ := colCell.getValueFrom(f, d)
|
||||||
|
if regSearch {
|
||||||
|
regex := regexp.MustCompile(value)
|
||||||
|
if !regex.MatchString(val) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if val != value {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, fmt.Sprintf("%s%d", strings.Map(letterOnlyMapF, colCell.R), r.R))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtectSheet provides a function to prevent other users from accidentally
|
||||||
|
// or deliberately changing, moving, or deleting data in a worksheet. For
|
||||||
|
// example, protect Sheet1 with protection settings:
|
||||||
|
//
|
||||||
|
// xlsx.ProtectSheet("Sheet1", &excelize.FormatSheetProtection{
|
||||||
|
// Password: "password",
|
||||||
|
// EditScenarios: false,
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
func (f *File) ProtectSheet(sheet string, settings *FormatSheetProtection) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if settings == nil {
|
||||||
|
settings = &FormatSheetProtection{
|
||||||
|
EditObjects: true,
|
||||||
|
EditScenarios: true,
|
||||||
|
SelectLockedCells: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlsx.SheetProtection = &xlsxSheetProtection{
|
||||||
|
AutoFilter: settings.AutoFilter,
|
||||||
|
DeleteColumns: settings.DeleteColumns,
|
||||||
|
DeleteRows: settings.DeleteRows,
|
||||||
|
FormatCells: settings.FormatCells,
|
||||||
|
FormatColumns: settings.FormatColumns,
|
||||||
|
FormatRows: settings.FormatRows,
|
||||||
|
InsertColumns: settings.InsertColumns,
|
||||||
|
InsertHyperlinks: settings.InsertHyperlinks,
|
||||||
|
InsertRows: settings.InsertRows,
|
||||||
|
Objects: settings.EditObjects,
|
||||||
|
PivotTables: settings.PivotTables,
|
||||||
|
Scenarios: settings.EditScenarios,
|
||||||
|
SelectLockedCells: settings.SelectLockedCells,
|
||||||
|
SelectUnlockedCells: settings.SelectUnlockedCells,
|
||||||
|
Sheet: true,
|
||||||
|
Sort: settings.Sort,
|
||||||
|
}
|
||||||
|
if settings.Password != "" {
|
||||||
|
xlsx.SheetProtection.Password = genSheetPasswd(settings.Password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnprotectSheet provides a function to unprotect an Excel worksheet.
|
||||||
|
func (f *File) UnprotectSheet(sheet string) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
xlsx.SheetProtection = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimSheetName provides a function to trim invaild characters by given worksheet
|
||||||
|
// name.
|
||||||
|
func trimSheetName(name string) string {
|
||||||
|
r := []rune{}
|
||||||
|
for _, v := range name {
|
||||||
|
switch v {
|
||||||
|
case 58, 92, 47, 63, 42, 91, 93: // replace :\/?*[]
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name = string(r)
|
||||||
|
if utf8.RuneCountInString(name) > 31 {
|
||||||
|
name = string([]rune(name)[0:31])
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
168
vendor/github.com/360EntSecGroup-Skylar/excelize/sheetpr.go
generated
vendored
Normal file
168
vendor/github.com/360EntSecGroup-Skylar/excelize/sheetpr.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
// SheetPrOption is an option of a view of a worksheet. See SetSheetPrOptions().
|
||||||
|
type SheetPrOption interface {
|
||||||
|
setSheetPrOption(view *xlsxSheetPr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SheetPrOptionPtr is a writable SheetPrOption. See GetSheetPrOptions().
|
||||||
|
type SheetPrOptionPtr interface {
|
||||||
|
SheetPrOption
|
||||||
|
getSheetPrOption(view *xlsxSheetPr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// CodeName is a SheetPrOption
|
||||||
|
CodeName string
|
||||||
|
// EnableFormatConditionsCalculation is a SheetPrOption
|
||||||
|
EnableFormatConditionsCalculation bool
|
||||||
|
// Published is a SheetPrOption
|
||||||
|
Published bool
|
||||||
|
// FitToPage is a SheetPrOption
|
||||||
|
FitToPage bool
|
||||||
|
// AutoPageBreaks is a SheetPrOption
|
||||||
|
AutoPageBreaks bool
|
||||||
|
// OutlineSummaryBelow is an outlinePr, within SheetPr option
|
||||||
|
OutlineSummaryBelow bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func (o OutlineSummaryBelow) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr.OutlinePr == nil {
|
||||||
|
pr.OutlinePr = new(xlsxOutlinePr)
|
||||||
|
}
|
||||||
|
pr.OutlinePr.SummaryBelow = bool(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OutlineSummaryBelow) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
// Excel default: true
|
||||||
|
if pr == nil || pr.OutlinePr == nil {
|
||||||
|
*o = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = OutlineSummaryBelow(defaultTrue(&pr.OutlinePr.SummaryBelow))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o CodeName) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
pr.CodeName = string(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *CodeName) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr == nil {
|
||||||
|
*o = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = CodeName(pr.CodeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o EnableFormatConditionsCalculation) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
pr.EnableFormatConditionsCalculation = boolPtr(bool(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *EnableFormatConditionsCalculation) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr == nil {
|
||||||
|
*o = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = EnableFormatConditionsCalculation(defaultTrue(pr.EnableFormatConditionsCalculation))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Published) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
pr.Published = boolPtr(bool(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Published) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr == nil {
|
||||||
|
*o = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = Published(defaultTrue(pr.Published))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o FitToPage) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr.PageSetUpPr == nil {
|
||||||
|
if !o {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pr.PageSetUpPr = new(xlsxPageSetUpPr)
|
||||||
|
}
|
||||||
|
pr.PageSetUpPr.FitToPage = bool(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *FitToPage) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
// Excel default: false
|
||||||
|
if pr == nil || pr.PageSetUpPr == nil {
|
||||||
|
*o = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = FitToPage(pr.PageSetUpPr.FitToPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o AutoPageBreaks) setSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
if pr.PageSetUpPr == nil {
|
||||||
|
if !o {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pr.PageSetUpPr = new(xlsxPageSetUpPr)
|
||||||
|
}
|
||||||
|
pr.PageSetUpPr.AutoPageBreaks = bool(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *AutoPageBreaks) getSheetPrOption(pr *xlsxSheetPr) {
|
||||||
|
// Excel default: false
|
||||||
|
if pr == nil || pr.PageSetUpPr == nil {
|
||||||
|
*o = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*o = AutoPageBreaks(pr.PageSetUpPr.AutoPageBreaks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetPrOptions provides a function to sets worksheet properties.
|
||||||
|
//
|
||||||
|
// Available options:
|
||||||
|
// CodeName(string)
|
||||||
|
// EnableFormatConditionsCalculation(bool)
|
||||||
|
// Published(bool)
|
||||||
|
// FitToPage(bool)
|
||||||
|
// AutoPageBreaks(bool)
|
||||||
|
// OutlineSummaryBelow(bool)
|
||||||
|
func (f *File) SetSheetPrOptions(name string, opts ...SheetPrOption) error {
|
||||||
|
sheet := f.workSheetReader(name)
|
||||||
|
pr := sheet.SheetPr
|
||||||
|
if pr == nil {
|
||||||
|
pr = new(xlsxSheetPr)
|
||||||
|
sheet.SheetPr = pr
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.setSheetPrOption(pr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetPrOptions provides a function to gets worksheet properties.
|
||||||
|
//
|
||||||
|
// Available options:
|
||||||
|
// CodeName(string)
|
||||||
|
// EnableFormatConditionsCalculation(bool)
|
||||||
|
// Published(bool)
|
||||||
|
// FitToPage(bool)
|
||||||
|
// AutoPageBreaks(bool)
|
||||||
|
// OutlineSummaryBelow(bool)
|
||||||
|
func (f *File) GetSheetPrOptions(name string, opts ...SheetPrOptionPtr) error {
|
||||||
|
sheet := f.workSheetReader(name)
|
||||||
|
pr := sheet.SheetPr
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.getSheetPrOption(pr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
171
vendor/github.com/360EntSecGroup-Skylar/excelize/sheetview.go
generated
vendored
Normal file
171
vendor/github.com/360EntSecGroup-Skylar/excelize/sheetview.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// SheetViewOption is an option of a view of a worksheet. See SetSheetViewOptions().
|
||||||
|
type SheetViewOption interface {
|
||||||
|
setSheetViewOption(view *xlsxSheetView)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SheetViewOptionPtr is a writable SheetViewOption. See GetSheetViewOptions().
|
||||||
|
type SheetViewOptionPtr interface {
|
||||||
|
SheetViewOption
|
||||||
|
getSheetViewOption(view *xlsxSheetView)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// DefaultGridColor is a SheetViewOption.
|
||||||
|
DefaultGridColor bool
|
||||||
|
// RightToLeft is a SheetViewOption.
|
||||||
|
RightToLeft bool
|
||||||
|
// ShowFormulas is a SheetViewOption.
|
||||||
|
ShowFormulas bool
|
||||||
|
// ShowGridLines is a SheetViewOption.
|
||||||
|
ShowGridLines bool
|
||||||
|
// ShowRowColHeaders is a SheetViewOption.
|
||||||
|
ShowRowColHeaders bool
|
||||||
|
// ZoomScale is a SheetViewOption.
|
||||||
|
ZoomScale float64
|
||||||
|
// TopLeftCell is a SheetViewOption.
|
||||||
|
TopLeftCell string
|
||||||
|
/* TODO
|
||||||
|
// ShowWhiteSpace is a SheetViewOption.
|
||||||
|
ShowWhiteSpace bool
|
||||||
|
// ShowZeros is a SheetViewOption.
|
||||||
|
ShowZeros bool
|
||||||
|
// WindowProtection is a SheetViewOption.
|
||||||
|
WindowProtection bool
|
||||||
|
*/
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defaults for each option are described in XML schema for CT_SheetView
|
||||||
|
|
||||||
|
func (o TopLeftCell) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.TopLeftCell = string(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TopLeftCell) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = TopLeftCell(string(view.TopLeftCell))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o DefaultGridColor) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.DefaultGridColor = boolPtr(bool(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DefaultGridColor) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = DefaultGridColor(defaultTrue(view.DefaultGridColor)) // Excel default: true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o RightToLeft) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.RightToLeft = bool(o) // Excel default: false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *RightToLeft) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = RightToLeft(view.RightToLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ShowFormulas) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.ShowFormulas = bool(o) // Excel default: false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ShowFormulas) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = ShowFormulas(view.ShowFormulas) // Excel default: false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ShowGridLines) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.ShowGridLines = boolPtr(bool(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ShowGridLines) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = ShowGridLines(defaultTrue(view.ShowGridLines)) // Excel default: true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ShowRowColHeaders) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
view.ShowRowColHeaders = boolPtr(bool(o))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ShowRowColHeaders) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = ShowRowColHeaders(defaultTrue(view.ShowRowColHeaders)) // Excel default: true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o ZoomScale) setSheetViewOption(view *xlsxSheetView) {
|
||||||
|
//This attribute is restricted to values ranging from 10 to 400.
|
||||||
|
if float64(o) >= 10 && float64(o) <= 400 {
|
||||||
|
view.ZoomScale = float64(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ZoomScale) getSheetViewOption(view *xlsxSheetView) {
|
||||||
|
*o = ZoomScale(view.ZoomScale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSheetView returns the SheetView object
|
||||||
|
func (f *File) getSheetView(sheetName string, viewIndex int) (*xlsxSheetView, error) {
|
||||||
|
xlsx := f.workSheetReader(sheetName)
|
||||||
|
if viewIndex < 0 {
|
||||||
|
if viewIndex < -len(xlsx.SheetViews.SheetView) {
|
||||||
|
return nil, fmt.Errorf("view index %d out of range", viewIndex)
|
||||||
|
}
|
||||||
|
viewIndex = len(xlsx.SheetViews.SheetView) + viewIndex
|
||||||
|
} else if viewIndex >= len(xlsx.SheetViews.SheetView) {
|
||||||
|
return nil, fmt.Errorf("view index %d out of range", viewIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(xlsx.SheetViews.SheetView[viewIndex]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSheetViewOptions sets sheet view options.
|
||||||
|
// The viewIndex may be negative and if so is counted backward (-1 is the last view).
|
||||||
|
//
|
||||||
|
// Available options:
|
||||||
|
// DefaultGridColor(bool)
|
||||||
|
// RightToLeft(bool)
|
||||||
|
// ShowFormulas(bool)
|
||||||
|
// ShowGridLines(bool)
|
||||||
|
// ShowRowColHeaders(bool)
|
||||||
|
// Example:
|
||||||
|
// err = f.SetSheetViewOptions("Sheet1", -1, ShowGridLines(false))
|
||||||
|
func (f *File) SetSheetViewOptions(name string, viewIndex int, opts ...SheetViewOption) error {
|
||||||
|
view, err := f.getSheetView(name, viewIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.setSheetViewOption(view)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSheetViewOptions gets the value of sheet view options.
|
||||||
|
// The viewIndex may be negative and if so is counted backward (-1 is the last view).
|
||||||
|
//
|
||||||
|
// Available options:
|
||||||
|
// DefaultGridColor(bool)
|
||||||
|
// RightToLeft(bool)
|
||||||
|
// ShowFormulas(bool)
|
||||||
|
// ShowGridLines(bool)
|
||||||
|
// ShowRowColHeaders(bool)
|
||||||
|
// Example:
|
||||||
|
// var showGridLines excelize.ShowGridLines
|
||||||
|
// err = f.GetSheetViewOptions("Sheet1", -1, &showGridLines)
|
||||||
|
func (f *File) GetSheetViewOptions(name string, viewIndex int, opts ...SheetViewOptionPtr) error {
|
||||||
|
view, err := f.getSheetView(name, viewIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.getSheetViewOption(view)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
2780
vendor/github.com/360EntSecGroup-Skylar/excelize/styles.go
generated
vendored
Normal file
2780
vendor/github.com/360EntSecGroup-Skylar/excelize/styles.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
461
vendor/github.com/360EntSecGroup-Skylar/excelize/table.go
generated
vendored
Normal file
461
vendor/github.com/360EntSecGroup-Skylar/excelize/table.go
generated
vendored
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseFormatTableSet provides a function to parse the format settings of the
|
||||||
|
// table with default value.
|
||||||
|
func parseFormatTableSet(formatSet string) (*formatTable, error) {
|
||||||
|
format := formatTable{
|
||||||
|
TableStyle: "",
|
||||||
|
ShowRowStripes: true,
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(parseFormatSet(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTable provides the method to add table in a worksheet by given worksheet
|
||||||
|
// name, coordinate area and format set. For example, create a table of A1:D5
|
||||||
|
// on Sheet1:
|
||||||
|
//
|
||||||
|
// xlsx.AddTable("Sheet1", "A1", "D5", ``)
|
||||||
|
//
|
||||||
|
// Create a table of F2:H6 on Sheet2 with format set:
|
||||||
|
//
|
||||||
|
// xlsx.AddTable("Sheet2", "F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2", "show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
|
||||||
|
//
|
||||||
|
// Note that the table at least two lines include string type header. Multiple
|
||||||
|
// tables coordinate areas can't have an intersection.
|
||||||
|
//
|
||||||
|
// table_name: The name of the table, in the same worksheet name of the table should be unique
|
||||||
|
//
|
||||||
|
// table_style: The built-in table style names
|
||||||
|
//
|
||||||
|
// TableStyleLight1 - TableStyleLight21
|
||||||
|
// TableStyleMedium1 - TableStyleMedium28
|
||||||
|
// TableStyleDark1 - TableStyleDark11
|
||||||
|
//
|
||||||
|
func (f *File) AddTable(sheet, hcell, vcell, format string) error {
|
||||||
|
formatSet, err := parseFormatTableSet(format)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hcell = strings.ToUpper(hcell)
|
||||||
|
vcell = strings.ToUpper(vcell)
|
||||||
|
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||||
|
hcol := string(strings.Map(letterOnlyMapF, hcell))
|
||||||
|
hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
|
||||||
|
hyAxis := hrow - 1
|
||||||
|
hxAxis := TitleToNumber(hcol)
|
||||||
|
|
||||||
|
vcol := string(strings.Map(letterOnlyMapF, vcell))
|
||||||
|
vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
|
||||||
|
vyAxis := vrow - 1
|
||||||
|
vxAxis := TitleToNumber(vcol)
|
||||||
|
if vxAxis < hxAxis {
|
||||||
|
vxAxis, hxAxis = hxAxis, vxAxis
|
||||||
|
}
|
||||||
|
if vyAxis < hyAxis {
|
||||||
|
vyAxis, hyAxis = hyAxis, vyAxis
|
||||||
|
}
|
||||||
|
tableID := f.countTables() + 1
|
||||||
|
sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
|
||||||
|
tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1)
|
||||||
|
// Add first table for given sheet.
|
||||||
|
rID := f.addSheetRelationships(sheet, SourceRelationshipTable, sheetRelationshipsTableXML, "")
|
||||||
|
f.addSheetTable(sheet, rID)
|
||||||
|
f.addTable(sheet, tableXML, hxAxis, hyAxis, vxAxis, vyAxis, tableID, formatSet)
|
||||||
|
f.addContentTypePart(tableID, "table")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// countTables provides a function to get table files count storage in the
|
||||||
|
// folder xl/tables.
|
||||||
|
func (f *File) countTables() int {
|
||||||
|
count := 0
|
||||||
|
for k := range f.XLSX {
|
||||||
|
if strings.Contains(k, "xl/tables/table") {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSheetTable provides a function to add tablePart element to
|
||||||
|
// xl/worksheets/sheet%d.xml by given worksheet name and relationship index.
|
||||||
|
func (f *File) addSheetTable(sheet string, rID int) {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
table := &xlsxTablePart{
|
||||||
|
RID: "rId" + strconv.Itoa(rID),
|
||||||
|
}
|
||||||
|
if xlsx.TableParts == nil {
|
||||||
|
xlsx.TableParts = &xlsxTableParts{}
|
||||||
|
}
|
||||||
|
xlsx.TableParts.Count++
|
||||||
|
xlsx.TableParts.TableParts = append(xlsx.TableParts.TableParts, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addTable provides a function to add table by given worksheet name,
|
||||||
|
// coordinate area and format set.
|
||||||
|
func (f *File) addTable(sheet, tableXML string, hxAxis, hyAxis, vxAxis, vyAxis, i int, formatSet *formatTable) {
|
||||||
|
// Correct the minimum number of rows, the table at least two lines.
|
||||||
|
if hyAxis == vyAxis {
|
||||||
|
vyAxis++
|
||||||
|
}
|
||||||
|
// Correct table reference coordinate area, such correct C1:B3 to B1:C3.
|
||||||
|
ref := ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||||
|
tableColumn := []*xlsxTableColumn{}
|
||||||
|
idx := 0
|
||||||
|
for i := hxAxis; i <= vxAxis; i++ {
|
||||||
|
idx++
|
||||||
|
cell := ToAlphaString(i) + strconv.Itoa(hyAxis+1)
|
||||||
|
name := f.GetCellValue(sheet, cell)
|
||||||
|
if _, err := strconv.Atoi(name); err == nil {
|
||||||
|
f.SetCellStr(sheet, cell, name)
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
name = "Column" + strconv.Itoa(idx)
|
||||||
|
f.SetCellStr(sheet, cell, name)
|
||||||
|
}
|
||||||
|
tableColumn = append(tableColumn, &xlsxTableColumn{
|
||||||
|
ID: idx,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
name := formatSet.TableName
|
||||||
|
if name == "" {
|
||||||
|
name = "Table" + strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
t := xlsxTable{
|
||||||
|
XMLNS: NameSpaceSpreadSheet,
|
||||||
|
ID: i,
|
||||||
|
Name: name,
|
||||||
|
DisplayName: name,
|
||||||
|
Ref: ref,
|
||||||
|
AutoFilter: &xlsxAutoFilter{
|
||||||
|
Ref: ref,
|
||||||
|
},
|
||||||
|
TableColumns: &xlsxTableColumns{
|
||||||
|
Count: idx,
|
||||||
|
TableColumn: tableColumn,
|
||||||
|
},
|
||||||
|
TableStyleInfo: &xlsxTableStyleInfo{
|
||||||
|
Name: formatSet.TableStyle,
|
||||||
|
ShowFirstColumn: formatSet.ShowFirstColumn,
|
||||||
|
ShowLastColumn: formatSet.ShowLastColumn,
|
||||||
|
ShowRowStripes: formatSet.ShowRowStripes,
|
||||||
|
ShowColumnStripes: formatSet.ShowColumnStripes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table, _ := xml.Marshal(t)
|
||||||
|
f.saveFileList(tableXML, table)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseAutoFilterSet provides a function to parse the settings of the auto
|
||||||
|
// filter.
|
||||||
|
func parseAutoFilterSet(formatSet string) (*formatAutoFilter, error) {
|
||||||
|
format := formatAutoFilter{}
|
||||||
|
err := json.Unmarshal([]byte(formatSet), &format)
|
||||||
|
return &format, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoFilter provides the method to add auto filter in a worksheet by given
|
||||||
|
// worksheet name, coordinate area and settings. An autofilter in Excel is a
|
||||||
|
// way of filtering a 2D range of data based on some simple criteria. For
|
||||||
|
// example applying an autofilter to a cell range A1:D4 in the Sheet1:
|
||||||
|
//
|
||||||
|
// err = xlsx.AutoFilter("Sheet1", "A1", "D4", "")
|
||||||
|
//
|
||||||
|
// Filter data in an autofilter:
|
||||||
|
//
|
||||||
|
// err = xlsx.AutoFilter("Sheet1", "A1", "D4", `{"column":"B","expression":"x != blanks"}`)
|
||||||
|
//
|
||||||
|
// column defines the filter columns in a autofilter range based on simple
|
||||||
|
// criteria
|
||||||
|
//
|
||||||
|
// It isn't sufficient to just specify the filter condition. You must also
|
||||||
|
// hide any rows that don't match the filter condition. Rows are hidden using
|
||||||
|
// the SetRowVisible() method. Excelize can't filter rows automatically since
|
||||||
|
// this isn't part of the file format.
|
||||||
|
//
|
||||||
|
// Setting a filter criteria for a column:
|
||||||
|
//
|
||||||
|
// expression defines the conditions, the following operators are available
|
||||||
|
// for setting the filter criteria:
|
||||||
|
//
|
||||||
|
// ==
|
||||||
|
// !=
|
||||||
|
// >
|
||||||
|
// <
|
||||||
|
// >=
|
||||||
|
// <=
|
||||||
|
// and
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// An expression can comprise a single statement or two statements separated
|
||||||
|
// by the 'and' and 'or' operators. For example:
|
||||||
|
//
|
||||||
|
// x < 2000
|
||||||
|
// x > 2000
|
||||||
|
// x == 2000
|
||||||
|
// x > 2000 and x < 5000
|
||||||
|
// x == 2000 or x == 5000
|
||||||
|
//
|
||||||
|
// Filtering of blank or non-blank data can be achieved by using a value of
|
||||||
|
// Blanks or NonBlanks in the expression:
|
||||||
|
//
|
||||||
|
// x == Blanks
|
||||||
|
// x == NonBlanks
|
||||||
|
//
|
||||||
|
// Excel also allows some simple string matching operations:
|
||||||
|
//
|
||||||
|
// x == b* // begins with b
|
||||||
|
// x != b* // doesnt begin with b
|
||||||
|
// x == *b // ends with b
|
||||||
|
// x != *b // doesnt end with b
|
||||||
|
// x == *b* // contains b
|
||||||
|
// x != *b* // doesn't contains b
|
||||||
|
//
|
||||||
|
// You can also use '*' to match any character or number and '?' to match any
|
||||||
|
// single character or number. No other regular expression quantifier is
|
||||||
|
// supported by Excel's filters. Excel's regular expression characters can be
|
||||||
|
// escaped using '~'.
|
||||||
|
//
|
||||||
|
// The placeholder variable x in the above examples can be replaced by any
|
||||||
|
// simple string. The actual placeholder name is ignored internally so the
|
||||||
|
// following are all equivalent:
|
||||||
|
//
|
||||||
|
// x < 2000
|
||||||
|
// col < 2000
|
||||||
|
// Price < 2000
|
||||||
|
//
|
||||||
|
func (f *File) AutoFilter(sheet, hcell, vcell, format string) error {
|
||||||
|
formatSet, _ := parseAutoFilterSet(format)
|
||||||
|
|
||||||
|
hcell = strings.ToUpper(hcell)
|
||||||
|
vcell = strings.ToUpper(vcell)
|
||||||
|
|
||||||
|
// Coordinate conversion, convert C1:B3 to 2,0,1,2.
|
||||||
|
hcol := string(strings.Map(letterOnlyMapF, hcell))
|
||||||
|
hrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, hcell))
|
||||||
|
hyAxis := hrow - 1
|
||||||
|
hxAxis := TitleToNumber(hcol)
|
||||||
|
|
||||||
|
vcol := string(strings.Map(letterOnlyMapF, vcell))
|
||||||
|
vrow, _ := strconv.Atoi(strings.Map(intOnlyMapF, vcell))
|
||||||
|
vyAxis := vrow - 1
|
||||||
|
vxAxis := TitleToNumber(vcol)
|
||||||
|
|
||||||
|
if vxAxis < hxAxis {
|
||||||
|
vxAxis, hxAxis = hxAxis, vxAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
if vyAxis < hyAxis {
|
||||||
|
vyAxis, hyAxis = hyAxis, vyAxis
|
||||||
|
}
|
||||||
|
ref := ToAlphaString(hxAxis) + strconv.Itoa(hyAxis+1) + ":" + ToAlphaString(vxAxis) + strconv.Itoa(vyAxis+1)
|
||||||
|
refRange := vxAxis - hxAxis
|
||||||
|
return f.autoFilter(sheet, ref, refRange, hxAxis, formatSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// autoFilter provides a function to extract the tokens from the filter
|
||||||
|
// expression. The tokens are mainly non-whitespace groups.
|
||||||
|
func (f *File) autoFilter(sheet, ref string, refRange, hxAxis int, formatSet *formatAutoFilter) error {
|
||||||
|
xlsx := f.workSheetReader(sheet)
|
||||||
|
if xlsx.SheetPr != nil {
|
||||||
|
xlsx.SheetPr.FilterMode = true
|
||||||
|
}
|
||||||
|
xlsx.SheetPr = &xlsxSheetPr{FilterMode: true}
|
||||||
|
filter := &xlsxAutoFilter{
|
||||||
|
Ref: ref,
|
||||||
|
}
|
||||||
|
xlsx.AutoFilter = filter
|
||||||
|
if formatSet.Column == "" || formatSet.Expression == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
col := TitleToNumber(formatSet.Column)
|
||||||
|
offset := col - hxAxis
|
||||||
|
if offset < 0 || offset > refRange {
|
||||||
|
return fmt.Errorf("incorrect index of column '%s'", formatSet.Column)
|
||||||
|
}
|
||||||
|
filter.FilterColumn = &xlsxFilterColumn{
|
||||||
|
ColID: offset,
|
||||||
|
}
|
||||||
|
re := regexp.MustCompile(`"(?:[^"]|"")*"|\S+`)
|
||||||
|
token := re.FindAllString(formatSet.Expression, -1)
|
||||||
|
if len(token) != 3 && len(token) != 7 {
|
||||||
|
return fmt.Errorf("incorrect number of tokens in criteria '%s'", formatSet.Expression)
|
||||||
|
}
|
||||||
|
expressions, tokens, err := f.parseFilterExpression(formatSet.Expression, token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.writeAutoFilter(filter, expressions, tokens)
|
||||||
|
xlsx.AutoFilter = filter
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeAutoFilter provides a function to check for single or double custom
|
||||||
|
// filters as default filters and handle them accordingly.
|
||||||
|
func (f *File) writeAutoFilter(filter *xlsxAutoFilter, exp []int, tokens []string) {
|
||||||
|
if len(exp) == 1 && exp[0] == 2 {
|
||||||
|
// Single equality.
|
||||||
|
filters := []*xlsxFilter{}
|
||||||
|
filters = append(filters, &xlsxFilter{Val: tokens[0]})
|
||||||
|
filter.FilterColumn.Filters = &xlsxFilters{Filter: filters}
|
||||||
|
} else if len(exp) == 3 && exp[0] == 2 && exp[1] == 1 && exp[2] == 2 {
|
||||||
|
// Double equality with "or" operator.
|
||||||
|
filters := []*xlsxFilter{}
|
||||||
|
for _, v := range tokens {
|
||||||
|
filters = append(filters, &xlsxFilter{Val: v})
|
||||||
|
}
|
||||||
|
filter.FilterColumn.Filters = &xlsxFilters{Filter: filters}
|
||||||
|
} else {
|
||||||
|
// Non default custom filter.
|
||||||
|
expRel := map[int]int{0: 0, 1: 2}
|
||||||
|
andRel := map[int]bool{0: true, 1: false}
|
||||||
|
for k, v := range tokens {
|
||||||
|
f.writeCustomFilter(filter, exp[expRel[k]], v)
|
||||||
|
if k == 1 {
|
||||||
|
filter.FilterColumn.CustomFilters.And = andRel[exp[k]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCustomFilter provides a function to write the <customFilter> element.
|
||||||
|
func (f *File) writeCustomFilter(filter *xlsxAutoFilter, operator int, val string) {
|
||||||
|
operators := map[int]string{
|
||||||
|
1: "lessThan",
|
||||||
|
2: "equal",
|
||||||
|
3: "lessThanOrEqual",
|
||||||
|
4: "greaterThan",
|
||||||
|
5: "notEqual",
|
||||||
|
6: "greaterThanOrEqual",
|
||||||
|
22: "equal",
|
||||||
|
}
|
||||||
|
customFilter := xlsxCustomFilter{
|
||||||
|
Operator: operators[operator],
|
||||||
|
Val: val,
|
||||||
|
}
|
||||||
|
if filter.FilterColumn.CustomFilters != nil {
|
||||||
|
filter.FilterColumn.CustomFilters.CustomFilter = append(filter.FilterColumn.CustomFilters.CustomFilter, &customFilter)
|
||||||
|
} else {
|
||||||
|
customFilters := []*xlsxCustomFilter{}
|
||||||
|
customFilters = append(customFilters, &customFilter)
|
||||||
|
filter.FilterColumn.CustomFilters = &xlsxCustomFilters{CustomFilter: customFilters}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFilterExpression provides a function to converts the tokens of a
|
||||||
|
// possibly conditional expression into 1 or 2 sub expressions for further
|
||||||
|
// parsing.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// ('x', '==', 2000) -> exp1
|
||||||
|
// ('x', '>', 2000, 'and', 'x', '<', 5000) -> exp1 and exp2
|
||||||
|
//
|
||||||
|
func (f *File) parseFilterExpression(expression string, tokens []string) ([]int, []string, error) {
|
||||||
|
expressions := []int{}
|
||||||
|
t := []string{}
|
||||||
|
if len(tokens) == 7 {
|
||||||
|
// The number of tokens will be either 3 (for 1 expression) or 7 (for 2
|
||||||
|
// expressions).
|
||||||
|
conditional := 0
|
||||||
|
c := tokens[3]
|
||||||
|
re, _ := regexp.Match(`(or|\|\|)`, []byte(c))
|
||||||
|
if re {
|
||||||
|
conditional = 1
|
||||||
|
}
|
||||||
|
expression1, token1, err := f.parseFilterTokens(expression, tokens[0:3])
|
||||||
|
if err != nil {
|
||||||
|
return expressions, t, err
|
||||||
|
}
|
||||||
|
expression2, token2, err := f.parseFilterTokens(expression, tokens[4:7])
|
||||||
|
if err != nil {
|
||||||
|
return expressions, t, err
|
||||||
|
}
|
||||||
|
expressions = []int{expression1[0], conditional, expression2[0]}
|
||||||
|
t = []string{token1, token2}
|
||||||
|
} else {
|
||||||
|
exp, token, err := f.parseFilterTokens(expression, tokens)
|
||||||
|
if err != nil {
|
||||||
|
return expressions, t, err
|
||||||
|
}
|
||||||
|
expressions = exp
|
||||||
|
t = []string{token}
|
||||||
|
}
|
||||||
|
return expressions, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFilterTokens provides a function to parse the 3 tokens of a filter
|
||||||
|
// expression and return the operator and token.
|
||||||
|
func (f *File) parseFilterTokens(expression string, tokens []string) ([]int, string, error) {
|
||||||
|
operators := map[string]int{
|
||||||
|
"==": 2,
|
||||||
|
"=": 2,
|
||||||
|
"=~": 2,
|
||||||
|
"eq": 2,
|
||||||
|
"!=": 5,
|
||||||
|
"!~": 5,
|
||||||
|
"ne": 5,
|
||||||
|
"<>": 5,
|
||||||
|
"<": 1,
|
||||||
|
"<=": 3,
|
||||||
|
">": 4,
|
||||||
|
">=": 6,
|
||||||
|
}
|
||||||
|
operator, ok := operators[strings.ToLower(tokens[1])]
|
||||||
|
if !ok {
|
||||||
|
// Convert the operator from a number to a descriptive string.
|
||||||
|
return []int{}, "", fmt.Errorf("unknown operator: %s", tokens[1])
|
||||||
|
}
|
||||||
|
token := tokens[2]
|
||||||
|
// Special handling for Blanks/NonBlanks.
|
||||||
|
re, _ := regexp.Match("blanks|nonblanks", []byte(strings.ToLower(token)))
|
||||||
|
if re {
|
||||||
|
// Only allow Equals or NotEqual in this context.
|
||||||
|
if operator != 2 && operator != 5 {
|
||||||
|
return []int{operator}, token, fmt.Errorf("the operator '%s' in expression '%s' is not valid in relation to Blanks/NonBlanks'", tokens[1], expression)
|
||||||
|
}
|
||||||
|
token = strings.ToLower(token)
|
||||||
|
// The operator should always be 2 (=) to flag a "simple" equality in
|
||||||
|
// the binary record. Therefore we convert <> to =.
|
||||||
|
if token == "blanks" {
|
||||||
|
if operator == 5 {
|
||||||
|
token = " "
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if operator == 5 {
|
||||||
|
operator = 2
|
||||||
|
token = "blanks"
|
||||||
|
} else {
|
||||||
|
operator = 5
|
||||||
|
token = " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the string token contains an Excel match character then change the
|
||||||
|
// operator type to indicate a non "simple" equality.
|
||||||
|
re, _ = regexp.Match("[*?]", []byte(token))
|
||||||
|
if operator == 2 && re {
|
||||||
|
operator = 22
|
||||||
|
}
|
||||||
|
return []int{operator}, token, nil
|
||||||
|
}
|
||||||
40
vendor/github.com/360EntSecGroup-Skylar/excelize/templates.go
generated
vendored
Normal file
40
vendor/github.com/360EntSecGroup-Skylar/excelize/templates.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
144
vendor/github.com/360EntSecGroup-Skylar/excelize/vmlDrawing.go
generated
vendored
Normal file
144
vendor/github.com/360EntSecGroup-Skylar/excelize/vmlDrawing.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// vmlDrawing directly maps the root element in the file
|
||||||
|
// xl/drawings/vmlDrawing%d.vml.
|
||||||
|
type vmlDrawing struct {
|
||||||
|
XMLName xml.Name `xml:"xml"`
|
||||||
|
XMLNSv string `xml:"xmlns:v,attr"`
|
||||||
|
XMLNSo string `xml:"xmlns:o,attr"`
|
||||||
|
XMLNSx string `xml:"xmlns:x,attr"`
|
||||||
|
XMLNSmv string `xml:"xmlns:mv,attr"`
|
||||||
|
Shapelayout *xlsxShapelayout `xml:"o:shapelayout"`
|
||||||
|
Shapetype *xlsxShapetype `xml:"v:shapetype"`
|
||||||
|
Shape []xlsxShape `xml:"v:shape"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxShapelayout directly maps the shapelayout element. This element contains
|
||||||
|
// child elements that store information used in the editing and layout of
|
||||||
|
// shapes.
|
||||||
|
type xlsxShapelayout struct {
|
||||||
|
Ext string `xml:"v:ext,attr"`
|
||||||
|
IDmap *xlsxIDmap `xml:"o:idmap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxIDmap directly maps the idmap element.
|
||||||
|
type xlsxIDmap struct {
|
||||||
|
Ext string `xml:"v:ext,attr"`
|
||||||
|
Data int `xml:"data,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxShape directly maps the shape element.
|
||||||
|
type xlsxShape struct {
|
||||||
|
XMLName xml.Name `xml:"v:shape"`
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Style string `xml:"style,attr"`
|
||||||
|
Fillcolor string `xml:"fillcolor,attr"`
|
||||||
|
Insetmode string `xml:"urn:schemas-microsoft-com:office:office insetmode,attr,omitempty"`
|
||||||
|
Strokecolor string `xml:"strokecolor,attr,omitempty"`
|
||||||
|
Val string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxShapetype directly maps the shapetype element.
|
||||||
|
type xlsxShapetype struct {
|
||||||
|
ID string `xml:"id,attr"`
|
||||||
|
Coordsize string `xml:"coordsize,attr"`
|
||||||
|
Spt int `xml:"o:spt,attr"`
|
||||||
|
Path string `xml:"path,attr"`
|
||||||
|
Stroke *xlsxStroke `xml:"v:stroke"`
|
||||||
|
VPath *vPath `xml:"v:path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxStroke directly maps the stroke element.
|
||||||
|
type xlsxStroke struct {
|
||||||
|
Joinstyle string `xml:"joinstyle,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// vPath directly maps the v:path element.
|
||||||
|
type vPath struct {
|
||||||
|
Gradientshapeok string `xml:"gradientshapeok,attr,omitempty"`
|
||||||
|
Connecttype string `xml:"o:connecttype,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// vFill directly maps the v:fill element. This element must be defined within a
|
||||||
|
// Shape element.
|
||||||
|
type vFill struct {
|
||||||
|
Angle int `xml:"angle,attr,omitempty"`
|
||||||
|
Color2 string `xml:"color2,attr"`
|
||||||
|
Type string `xml:"type,attr,omitempty"`
|
||||||
|
Fill *oFill `xml:"o:fill"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// oFill directly maps the o:fill element.
|
||||||
|
type oFill struct {
|
||||||
|
Ext string `xml:"v:ext,attr"`
|
||||||
|
Type string `xml:"type,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// vShadow directly maps the v:shadow element. This element must be defined
|
||||||
|
// within a Shape element. In addition, the On attribute must be set to True.
|
||||||
|
type vShadow struct {
|
||||||
|
On string `xml:"on,attr"`
|
||||||
|
Color string `xml:"color,attr,omitempty"`
|
||||||
|
Obscured string `xml:"obscured,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// vTextbox directly maps the v:textbox element. This element must be defined
|
||||||
|
// within a Shape element.
|
||||||
|
type vTextbox struct {
|
||||||
|
Style string `xml:"style,attr"`
|
||||||
|
Div *xlsxDiv `xml:"div"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxDiv directly maps the div element.
|
||||||
|
type xlsxDiv struct {
|
||||||
|
Style string `xml:"style,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xClientData (Attached Object Data) directly maps the x:ClientData element.
|
||||||
|
// This element specifies data associated with objects attached to a
|
||||||
|
// spreadsheet. While this element might contain any of the child elements
|
||||||
|
// below, only certain combinations are meaningful. The ObjectType attribute
|
||||||
|
// determines the kind of object the element represents and which subset of
|
||||||
|
// child elements is appropriate. Relevant groups are identified for each child
|
||||||
|
// element.
|
||||||
|
type xClientData struct {
|
||||||
|
ObjectType string `xml:"ObjectType,attr"`
|
||||||
|
MoveWithCells string `xml:"x:MoveWithCells,omitempty"`
|
||||||
|
SizeWithCells string `xml:"x:SizeWithCells,omitempty"`
|
||||||
|
Anchor string `xml:"x:Anchor"`
|
||||||
|
AutoFill string `xml:"x:AutoFill"`
|
||||||
|
Row int `xml:"x:Row"`
|
||||||
|
Column int `xml:"x:Column"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeVmlDrawing defines the structure used to parse the file
|
||||||
|
// xl/drawings/vmlDrawing%d.vml.
|
||||||
|
type decodeVmlDrawing struct {
|
||||||
|
Shape []decodeShape `xml:"urn:schemas-microsoft-com:vml shape"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeShape defines the structure used to parse the particular shape element.
|
||||||
|
type decodeShape struct {
|
||||||
|
Val string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeShape defines the structure used to re-serialization shape element.
|
||||||
|
type encodeShape struct {
|
||||||
|
Fill *vFill `xml:"v:fill"`
|
||||||
|
Shadow *vShadow `xml:"v:shadow"`
|
||||||
|
Path *vPath `xml:"v:path"`
|
||||||
|
Textbox *vTextbox `xml:"v:textbox"`
|
||||||
|
ClientData *xClientData `xml:"x:ClientData"`
|
||||||
|
}
|
||||||
623
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlChart.go
generated
vendored
Normal file
623
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlChart.go
generated
vendored
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// xlsxChartSpace directly maps the c:chartSpace element. The chart namespace in
|
||||||
|
// DrawingML is for representing visualizations of numeric data with column
|
||||||
|
// charts, pie charts, scatter charts, or other types of charts.
|
||||||
|
type xlsxChartSpace struct {
|
||||||
|
XMLName xml.Name `xml:"c:chartSpace"`
|
||||||
|
XMLNSc string `xml:"xmlns:c,attr"`
|
||||||
|
XMLNSa string `xml:"xmlns:a,attr"`
|
||||||
|
XMLNSr string `xml:"xmlns:r,attr"`
|
||||||
|
XMLNSc16r2 string `xml:"xmlns:c16r2,attr"`
|
||||||
|
Date1904 *attrValBool `xml:"c:date1904"`
|
||||||
|
Lang *attrValString `xml:"c:lang"`
|
||||||
|
RoundedCorners *attrValBool `xml:"c:roundedCorners"`
|
||||||
|
Chart cChart `xml:"c:chart"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
TxPr *cTxPr `xml:"c:txPr"`
|
||||||
|
PrintSettings *cPrintSettings `xml:"c:printSettings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cThicknessSpPr directly maps the element that specifies the thickness of the
|
||||||
|
// walls or floor as a percentage of the largest dimension of the plot volume
|
||||||
|
// and SpPr element.
|
||||||
|
type cThicknessSpPr struct {
|
||||||
|
Thickness *attrValInt `xml:"c:thickness"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cChart (Chart) directly maps the c:chart element. This element specifies a
|
||||||
|
// title.
|
||||||
|
type cChart struct {
|
||||||
|
Title *cTitle `xml:"c:title"`
|
||||||
|
AutoTitleDeleted *cAutoTitleDeleted `xml:"c:autoTitleDeleted"`
|
||||||
|
View3D *cView3D `xml:"c:view3D"`
|
||||||
|
Floor *cThicknessSpPr `xml:"c:floor"`
|
||||||
|
SideWall *cThicknessSpPr `xml:"c:sideWall"`
|
||||||
|
BackWall *cThicknessSpPr `xml:"c:backWall"`
|
||||||
|
PlotArea *cPlotArea `xml:"c:plotArea"`
|
||||||
|
Legend *cLegend `xml:"c:legend"`
|
||||||
|
PlotVisOnly *attrValBool `xml:"c:plotVisOnly"`
|
||||||
|
DispBlanksAs *attrValString `xml:"c:dispBlanksAs"`
|
||||||
|
ShowDLblsOverMax *attrValBool `xml:"c:showDLblsOverMax"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cTitle (Title) directly maps the c:title element. This element specifies a
|
||||||
|
// title.
|
||||||
|
type cTitle struct {
|
||||||
|
Tx cTx `xml:"c:tx,omitempty"`
|
||||||
|
Layout string `xml:"c:layout,omitempty"`
|
||||||
|
Overlay attrValBool `xml:"c:overlay,omitempty"`
|
||||||
|
SpPr cSpPr `xml:"c:spPr,omitempty"`
|
||||||
|
TxPr cTxPr `xml:"c:txPr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cTx (Chart Text) directly maps the c:tx element. This element specifies text
|
||||||
|
// to use on a chart, including rich text formatting.
|
||||||
|
type cTx struct {
|
||||||
|
StrRef *cStrRef `xml:"c:strRef"`
|
||||||
|
Rich *cRich `xml:"c:rich,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cRich (Rich Text) directly maps the c:rich element. This element contains a
|
||||||
|
// string with rich text formatting.
|
||||||
|
type cRich struct {
|
||||||
|
BodyPr aBodyPr `xml:"a:bodyPr,omitempty"`
|
||||||
|
LstStyle string `xml:"a:lstStyle,omitempty"`
|
||||||
|
P aP `xml:"a:p"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aBodyPr (Body Properties) directly maps the a:bodyPr element. This element
|
||||||
|
// defines the body properties for the text body within a shape.
|
||||||
|
type aBodyPr struct {
|
||||||
|
Anchor string `xml:"anchor,attr,omitempty"`
|
||||||
|
AnchorCtr bool `xml:"anchorCtr,attr"`
|
||||||
|
Rot int `xml:"rot,attr"`
|
||||||
|
BIns float64 `xml:"bIns,attr,omitempty"`
|
||||||
|
CompatLnSpc bool `xml:"compatLnSpc,attr,omitempty"`
|
||||||
|
ForceAA bool `xml:"forceAA,attr,omitempty"`
|
||||||
|
FromWordArt bool `xml:"fromWordArt,attr,omitempty"`
|
||||||
|
HorzOverflow string `xml:"horzOverflow,attr,omitempty"`
|
||||||
|
LIns float64 `xml:"lIns,attr,omitempty"`
|
||||||
|
NumCol int `xml:"numCol,attr,omitempty"`
|
||||||
|
RIns float64 `xml:"rIns,attr,omitempty"`
|
||||||
|
RtlCol bool `xml:"rtlCol,attr,omitempty"`
|
||||||
|
SpcCol int `xml:"spcCol,attr,omitempty"`
|
||||||
|
SpcFirstLastPara bool `xml:"spcFirstLastPara,attr"`
|
||||||
|
TIns float64 `xml:"tIns,attr,omitempty"`
|
||||||
|
Upright bool `xml:"upright,attr,omitempty"`
|
||||||
|
Vert string `xml:"vert,attr,omitempty"`
|
||||||
|
VertOverflow string `xml:"vertOverflow,attr,omitempty"`
|
||||||
|
Wrap string `xml:"wrap,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aP (Paragraph) directly maps the a:p element. This element specifies a
|
||||||
|
// paragraph of content in the document.
|
||||||
|
type aP struct {
|
||||||
|
PPr *aPPr `xml:"a:pPr"`
|
||||||
|
R *aR `xml:"a:r"`
|
||||||
|
EndParaRPr *aEndParaRPr `xml:"a:endParaRPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aPPr (Paragraph Properties) directly maps the a:pPr element. This element
|
||||||
|
// specifies a set of paragraph properties which shall be applied to the
|
||||||
|
// contents of the parent paragraph after all style/numbering/table properties
|
||||||
|
// have been applied to the text. These properties are defined as direct
|
||||||
|
// formatting, since they are directly applied to the paragraph and supersede
|
||||||
|
// any formatting from styles.
|
||||||
|
type aPPr struct {
|
||||||
|
DefRPr aRPr `xml:"a:defRPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aSolidFill (Solid Fill) directly maps the solidFill element. This element
|
||||||
|
// specifies a solid color fill. The shape is filled entirely with the specified
|
||||||
|
// color.
|
||||||
|
type aSolidFill struct {
|
||||||
|
SchemeClr *aSchemeClr `xml:"a:schemeClr"`
|
||||||
|
SrgbClr *attrValString `xml:"a:srgbClr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aSchemeClr (Scheme Color) directly maps the a:schemeClr element. This
|
||||||
|
// element specifies a color bound to a user's theme. As with all elements which
|
||||||
|
// define a color, it is possible to apply a list of color transforms to the
|
||||||
|
// base color defined.
|
||||||
|
type aSchemeClr struct {
|
||||||
|
Val string `xml:"val,attr,omitempty"`
|
||||||
|
LumMod *attrValInt `xml:"a:lumMod"`
|
||||||
|
LumOff *attrValInt `xml:"a:lumOff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// attrValInt directly maps the val element with integer data type as an
|
||||||
|
// attribute。
|
||||||
|
type attrValInt struct {
|
||||||
|
Val int `xml:"val,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// attrValFloat directly maps the val element with float64 data type as an
|
||||||
|
// attribute。
|
||||||
|
type attrValFloat struct {
|
||||||
|
Val float64 `xml:"val,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// attrValBool directly maps the val element with boolean data type as an
|
||||||
|
// attribute。
|
||||||
|
type attrValBool struct {
|
||||||
|
Val bool `xml:"val,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// attrValString directly maps the val element with string data type as an
|
||||||
|
// attribute。
|
||||||
|
type attrValString struct {
|
||||||
|
Val string `xml:"val,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aCs directly maps the a:cs element.
|
||||||
|
type aCs struct {
|
||||||
|
Typeface string `xml:"typeface,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aEa directly maps the a:ea element.
|
||||||
|
type aEa struct {
|
||||||
|
Typeface string `xml:"typeface,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aLatin (Latin Font) directly maps the a:latin element. This element
|
||||||
|
// specifies that a Latin font be used for a specific run of text. This font is
|
||||||
|
// specified with a typeface attribute much like the others but is specifically
|
||||||
|
// classified as a Latin font.
|
||||||
|
type aLatin struct {
|
||||||
|
Typeface string `xml:"typeface,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aR directly maps the a:r element.
|
||||||
|
type aR struct {
|
||||||
|
RPr aRPr `xml:"a:rPr,omitempty"`
|
||||||
|
T string `xml:"a:t,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aRPr (Run Properties) directly maps the c:rPr element. This element
|
||||||
|
// specifies a set of run properties which shall be applied to the contents of
|
||||||
|
// the parent run after all style formatting has been applied to the text. These
|
||||||
|
// properties are defined as direct formatting, since they are directly applied
|
||||||
|
// to the run and supersede any formatting from styles.
|
||||||
|
type aRPr struct {
|
||||||
|
AltLang string `xml:"altLang,attr,omitempty"`
|
||||||
|
B bool `xml:"b,attr"`
|
||||||
|
Baseline int `xml:"baseline,attr"`
|
||||||
|
Bmk string `xml:"bmk,attr,omitempty"`
|
||||||
|
Cap string `xml:"cap,attr,omitempty"`
|
||||||
|
Dirty bool `xml:"dirty,attr,omitempty"`
|
||||||
|
Err bool `xml:"err,attr,omitempty"`
|
||||||
|
I bool `xml:"i,attr"`
|
||||||
|
Kern int `xml:"kern,attr"`
|
||||||
|
Kumimoji bool `xml:"kumimoji,attr,omitempty"`
|
||||||
|
Lang string `xml:"lang,attr,omitempty"`
|
||||||
|
NoProof bool `xml:"noProof,attr,omitempty"`
|
||||||
|
NormalizeH bool `xml:"normalizeH,attr,omitempty"`
|
||||||
|
SmtClean bool `xml:"smtClean,attr,omitempty"`
|
||||||
|
SmtID uint64 `xml:"smtId,attr,omitempty"`
|
||||||
|
Spc int `xml:"spc,attr"`
|
||||||
|
Strike string `xml:"strike,attr,omitempty"`
|
||||||
|
Sz int `xml:"sz,attr,omitempty"`
|
||||||
|
U string `xml:"u,attr,omitempty"`
|
||||||
|
SolidFill *aSolidFill `xml:"a:solidFill"`
|
||||||
|
Latin *aLatin `xml:"a:latin"`
|
||||||
|
Ea *aEa `xml:"a:ea"`
|
||||||
|
Cs *aCs `xml:"a:cs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cSpPr (Shape Properties) directly maps the c:spPr element. This element
|
||||||
|
// specifies the visual shape properties that can be applied to a shape. These
|
||||||
|
// properties include the shape fill, outline, geometry, effects, and 3D
|
||||||
|
// orientation.
|
||||||
|
type cSpPr struct {
|
||||||
|
NoFill *string `xml:"a:noFill"`
|
||||||
|
SolidFill *aSolidFill `xml:"a:solidFill"`
|
||||||
|
Ln *aLn `xml:"a:ln"`
|
||||||
|
Sp3D *aSp3D `xml:"a:sp3d"`
|
||||||
|
EffectLst *string `xml:"a:effectLst"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aSp3D (3-D Shape Properties) directly maps the a:sp3d element. This element
|
||||||
|
// defines the 3D properties associated with a particular shape in DrawingML.
|
||||||
|
// The 3D properties which can be applied to a shape are top and bottom bevels,
|
||||||
|
// a contour and an extrusion.
|
||||||
|
type aSp3D struct {
|
||||||
|
ContourW int `xml:"contourW,attr"`
|
||||||
|
ContourClr *aContourClr `xml:"a:contourClr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aContourClr (Contour Color) directly maps the a:contourClr element. This
|
||||||
|
// element defines the color for the contour on a shape. The contour of a shape
|
||||||
|
// is a solid filled line which surrounds the outer edges of the shape.
|
||||||
|
type aContourClr struct {
|
||||||
|
SchemeClr *aSchemeClr `xml:"a:schemeClr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aLn (Outline) directly maps the a:ln element. This element specifies an
|
||||||
|
// outline style that can be applied to a number of different objects such as
|
||||||
|
// shapes and text. The line allows for the specifying of many different types
|
||||||
|
// of outlines including even line dashes and bevels.
|
||||||
|
type aLn struct {
|
||||||
|
Algn string `xml:"algn,attr,omitempty"`
|
||||||
|
Cap string `xml:"cap,attr,omitempty"`
|
||||||
|
Cmpd string `xml:"cmpd,attr,omitempty"`
|
||||||
|
W int `xml:"w,attr,omitempty"`
|
||||||
|
NoFill string `xml:"a:noFill,omitempty"`
|
||||||
|
Round string `xml:"a:round,omitempty"`
|
||||||
|
SolidFill *aSolidFill `xml:"a:solidFill"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cTxPr (Text Properties) directly maps the c:txPr element. This element
|
||||||
|
// specifies text formatting. The lstStyle element is not supported.
|
||||||
|
type cTxPr struct {
|
||||||
|
BodyPr aBodyPr `xml:"a:bodyPr,omitempty"`
|
||||||
|
LstStyle string `xml:"a:lstStyle,omitempty"`
|
||||||
|
P aP `xml:"a:p,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aEndParaRPr (End Paragraph Run Properties) directly maps the a:endParaRPr
|
||||||
|
// element. This element specifies the text run properties that are to be used
|
||||||
|
// if another run is inserted after the last run specified. This effectively
|
||||||
|
// saves the run property state so that it can be applied when the user enters
|
||||||
|
// additional text. If this element is omitted, then the application can
|
||||||
|
// determine which default properties to apply. It is recommended that this
|
||||||
|
// element be specified at the end of the list of text runs within the paragraph
|
||||||
|
// so that an orderly list is maintained.
|
||||||
|
type aEndParaRPr struct {
|
||||||
|
Lang string `xml:"lang,attr"`
|
||||||
|
AltLang string `xml:"altLang,attr,omitempty"`
|
||||||
|
Sz int `xml:"sz,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cAutoTitleDeleted (Auto Title Is Deleted) directly maps the
|
||||||
|
// c:autoTitleDeleted element. This element specifies the title shall not be
|
||||||
|
// shown for this chart.
|
||||||
|
type cAutoTitleDeleted struct {
|
||||||
|
Val bool `xml:"val,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cView3D (View In 3D) directly maps the c:view3D element. This element
|
||||||
|
// specifies the 3-D view of the chart.
|
||||||
|
type cView3D struct {
|
||||||
|
RotX *attrValInt `xml:"c:rotX"`
|
||||||
|
RotY *attrValInt `xml:"c:rotY"`
|
||||||
|
DepthPercent *attrValInt `xml:"c:depthPercent"`
|
||||||
|
RAngAx *attrValInt `xml:"c:rAngAx"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cPlotArea directly maps the c:plotArea element. This element specifies the
|
||||||
|
// plot area of the chart.
|
||||||
|
type cPlotArea struct {
|
||||||
|
Layout *string `xml:"c:layout"`
|
||||||
|
AreaChart *cCharts `xml:"c:areaChart"`
|
||||||
|
Area3DChart *cCharts `xml:"c:area3DChart"`
|
||||||
|
BarChart *cCharts `xml:"c:barChart"`
|
||||||
|
Bar3DChart *cCharts `xml:"c:bar3DChart"`
|
||||||
|
DoughnutChart *cCharts `xml:"c:doughnutChart"`
|
||||||
|
LineChart *cCharts `xml:"c:lineChart"`
|
||||||
|
PieChart *cCharts `xml:"c:pieChart"`
|
||||||
|
Pie3DChart *cCharts `xml:"c:pie3DChart"`
|
||||||
|
RadarChart *cCharts `xml:"c:radarChart"`
|
||||||
|
ScatterChart *cCharts `xml:"c:scatterChart"`
|
||||||
|
CatAx []*cAxs `xml:"c:catAx"`
|
||||||
|
ValAx []*cAxs `xml:"c:valAx"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cCharts specifies the common element of the chart.
|
||||||
|
type cCharts struct {
|
||||||
|
BarDir *attrValString `xml:"c:barDir"`
|
||||||
|
Grouping *attrValString `xml:"c:grouping"`
|
||||||
|
RadarStyle *attrValString `xml:"c:radarStyle"`
|
||||||
|
ScatterStyle *attrValString `xml:"c:scatterStyle"`
|
||||||
|
VaryColors *attrValBool `xml:"c:varyColors"`
|
||||||
|
Ser *[]cSer `xml:"c:ser"`
|
||||||
|
DLbls *cDLbls `xml:"c:dLbls"`
|
||||||
|
HoleSize *attrValInt `xml:"c:holeSize"`
|
||||||
|
Smooth *attrValBool `xml:"c:smooth"`
|
||||||
|
Overlap *attrValInt `xml:"c:overlap"`
|
||||||
|
AxID []*attrValInt `xml:"c:axId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cAxs directly maps the c:catAx and c:valAx element.
|
||||||
|
type cAxs struct {
|
||||||
|
AxID *attrValInt `xml:"c:axId"`
|
||||||
|
Scaling *cScaling `xml:"c:scaling"`
|
||||||
|
Delete *attrValBool `xml:"c:delete"`
|
||||||
|
AxPos *attrValString `xml:"c:axPos"`
|
||||||
|
NumFmt *cNumFmt `xml:"c:numFmt"`
|
||||||
|
MajorTickMark *attrValString `xml:"c:majorTickMark"`
|
||||||
|
MinorTickMark *attrValString `xml:"c:minorTickMark"`
|
||||||
|
TickLblPos *attrValString `xml:"c:tickLblPos"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
TxPr *cTxPr `xml:"c:txPr"`
|
||||||
|
CrossAx *attrValInt `xml:"c:crossAx"`
|
||||||
|
Crosses *attrValString `xml:"c:crosses"`
|
||||||
|
CrossBetween *attrValString `xml:"c:crossBetween"`
|
||||||
|
Auto *attrValBool `xml:"c:auto"`
|
||||||
|
LblAlgn *attrValString `xml:"c:lblAlgn"`
|
||||||
|
LblOffset *attrValInt `xml:"c:lblOffset"`
|
||||||
|
NoMultiLvlLbl *attrValBool `xml:"c:noMultiLvlLbl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cScaling directly maps the c:scaling element. This element contains
|
||||||
|
// additional axis settings.
|
||||||
|
type cScaling struct {
|
||||||
|
Orientation *attrValString `xml:"c:orientation"`
|
||||||
|
Max *attrValFloat `xml:"c:max"`
|
||||||
|
Min *attrValFloat `xml:"c:min"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cNumFmt (Numbering Format) directly maps the c:numFmt element. This element
|
||||||
|
// specifies number formatting for the parent element.
|
||||||
|
type cNumFmt struct {
|
||||||
|
FormatCode string `xml:"formatCode,attr"`
|
||||||
|
SourceLinked bool `xml:"sourceLinked,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cSer directly maps the c:ser element. This element specifies a series on a
|
||||||
|
// chart.
|
||||||
|
type cSer struct {
|
||||||
|
IDx *attrValInt `xml:"c:idx"`
|
||||||
|
Order *attrValInt `xml:"c:order"`
|
||||||
|
Tx *cTx `xml:"c:tx"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
DPt []*cDPt `xml:"c:dPt"`
|
||||||
|
DLbls *cDLbls `xml:"c:dLbls"`
|
||||||
|
Marker *cMarker `xml:"c:marker"`
|
||||||
|
InvertIfNegative *attrValBool `xml:"c:invertIfNegative"`
|
||||||
|
Cat *cCat `xml:"c:cat"`
|
||||||
|
Val *cVal `xml:"c:val"`
|
||||||
|
XVal *cCat `xml:"c:xVal"`
|
||||||
|
YVal *cVal `xml:"c:yVal"`
|
||||||
|
Smooth *attrValBool `xml:"c:smooth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cMarker (Marker) directly maps the c:marker element. This element specifies a
|
||||||
|
// data marker.
|
||||||
|
type cMarker struct {
|
||||||
|
Symbol *attrValString `xml:"c:symbol"`
|
||||||
|
Size *attrValInt `xml:"c:size"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cDPt (Data Point) directly maps the c:dPt element. This element specifies a
|
||||||
|
// single data point.
|
||||||
|
type cDPt struct {
|
||||||
|
IDx *attrValInt `xml:"c:idx"`
|
||||||
|
Bubble3D *attrValBool `xml:"c:bubble3D"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cCat (Category Axis Data) directly maps the c:cat element. This element
|
||||||
|
// specifies the data used for the category axis.
|
||||||
|
type cCat struct {
|
||||||
|
StrRef *cStrRef `xml:"c:strRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cStrRef (String Reference) directly maps the c:strRef element. This element
|
||||||
|
// specifies a reference to data for a single data label or title with a cache
|
||||||
|
// of the last values used.
|
||||||
|
type cStrRef struct {
|
||||||
|
F string `xml:"c:f"`
|
||||||
|
StrCache *cStrCache `xml:"c:strCache"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cStrCache (String Cache) directly maps the c:strCache element. This element
|
||||||
|
// specifies the last string data used for a chart.
|
||||||
|
type cStrCache struct {
|
||||||
|
Pt []*cPt `xml:"c:pt"`
|
||||||
|
PtCount *attrValInt `xml:"c:ptCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cPt directly maps the c:pt element. This element specifies data for a
|
||||||
|
// particular data point.
|
||||||
|
type cPt struct {
|
||||||
|
IDx int `xml:"idx,attr"`
|
||||||
|
V *string `xml:"c:v"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cVal directly maps the c:val element. This element specifies the data values
|
||||||
|
// which shall be used to define the location of data markers on a chart.
|
||||||
|
type cVal struct {
|
||||||
|
NumRef *cNumRef `xml:"c:numRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cNumRef directly maps the c:numRef element. This element specifies a
|
||||||
|
// reference to numeric data with a cache of the last values used.
|
||||||
|
type cNumRef struct {
|
||||||
|
F string `xml:"c:f"`
|
||||||
|
NumCache *cNumCache `xml:"c:numCache"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cNumCache directly maps the c:numCache element. This element specifies the
|
||||||
|
// last data shown on the chart for a series.
|
||||||
|
type cNumCache struct {
|
||||||
|
FormatCode string `xml:"c:formatCode"`
|
||||||
|
Pt []*cPt `xml:"c:pt"`
|
||||||
|
PtCount *attrValInt `xml:"c:ptCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cDLbls (Data Lables) directly maps the c:dLbls element. This element serves
|
||||||
|
// as a root element that specifies the settings for the data labels for an
|
||||||
|
// entire series or the entire chart. It contains child elements that specify
|
||||||
|
// the specific formatting and positioning settings.
|
||||||
|
type cDLbls struct {
|
||||||
|
ShowLegendKey *attrValBool `xml:"c:showLegendKey"`
|
||||||
|
ShowVal *attrValBool `xml:"c:showVal"`
|
||||||
|
ShowCatName *attrValBool `xml:"c:showCatName"`
|
||||||
|
ShowSerName *attrValBool `xml:"c:showSerName"`
|
||||||
|
ShowPercent *attrValBool `xml:"c:showPercent"`
|
||||||
|
ShowBubbleSize *attrValBool `xml:"c:showBubbleSize"`
|
||||||
|
ShowLeaderLines *attrValBool `xml:"c:showLeaderLines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cLegend (Legend) directly maps the c:legend element. This element specifies
|
||||||
|
// the legend.
|
||||||
|
type cLegend struct {
|
||||||
|
Layout *string `xml:"c:layout"`
|
||||||
|
LegendPos *attrValString `xml:"c:legendPos"`
|
||||||
|
Overlay *attrValBool `xml:"c:overlay"`
|
||||||
|
SpPr *cSpPr `xml:"c:spPr"`
|
||||||
|
TxPr *cTxPr `xml:"c:txPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cPrintSettings directly maps the c:printSettings element. This element
|
||||||
|
// specifies the print settings for the chart.
|
||||||
|
type cPrintSettings struct {
|
||||||
|
HeaderFooter *string `xml:"c:headerFooter"`
|
||||||
|
PageMargins *cPageMargins `xml:"c:pageMargins"`
|
||||||
|
PageSetup *string `xml:"c:pageSetup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// cPageMargins directly maps the c:pageMargins element. This element specifies
|
||||||
|
// the page margins for a chart.
|
||||||
|
type cPageMargins struct {
|
||||||
|
B float64 `xml:"b,attr"`
|
||||||
|
Footer float64 `xml:"footer,attr"`
|
||||||
|
Header float64 `xml:"header,attr"`
|
||||||
|
L float64 `xml:"l,attr"`
|
||||||
|
R float64 `xml:"r,attr"`
|
||||||
|
T float64 `xml:"t,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatChartAxis directly maps the format settings of the chart axis.
|
||||||
|
type formatChartAxis struct {
|
||||||
|
Crossing string `json:"crossing"`
|
||||||
|
MajorTickMark string `json:"major_tick_mark"`
|
||||||
|
MinorTickMark string `json:"minor_tick_mark"`
|
||||||
|
MinorUnitType string `json:"minor_unit_type"`
|
||||||
|
MajorUnit int `json:"major_unit"`
|
||||||
|
MajorUnitType string `json:"major_unit_type"`
|
||||||
|
DisplayUnits string `json:"display_units"`
|
||||||
|
DisplayUnitsVisible bool `json:"display_units_visible"`
|
||||||
|
DateAxis bool `json:"date_axis"`
|
||||||
|
ReverseOrder bool `json:"reverse_order"`
|
||||||
|
Maximum float64 `json:"maximum"`
|
||||||
|
Minimum float64 `json:"minimum"`
|
||||||
|
NumFormat string `json:"num_format"`
|
||||||
|
NumFont struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
Bold bool `json:"bold"`
|
||||||
|
Italic bool `json:"italic"`
|
||||||
|
Underline bool `json:"underline"`
|
||||||
|
} `json:"num_font"`
|
||||||
|
NameLayout formatLayout `json:"name_layout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type formatChartDimension struct {
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatChart directly maps the format settings of the chart.
|
||||||
|
type formatChart struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Series []formatChartSeries `json:"series"`
|
||||||
|
Format formatPicture `json:"format"`
|
||||||
|
Dimension formatChartDimension `json:"dimension"`
|
||||||
|
Legend formatChartLegend `json:"legend"`
|
||||||
|
Title formatChartTitle `json:"title"`
|
||||||
|
XAxis formatChartAxis `json:"x_axis"`
|
||||||
|
YAxis formatChartAxis `json:"y_axis"`
|
||||||
|
Chartarea struct {
|
||||||
|
Border struct {
|
||||||
|
None bool `json:"none"`
|
||||||
|
} `json:"border"`
|
||||||
|
Fill struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
} `json:"fill"`
|
||||||
|
Pattern struct {
|
||||||
|
Pattern string `json:"pattern"`
|
||||||
|
FgColor string `json:"fg_color"`
|
||||||
|
BgColor string `json:"bg_color"`
|
||||||
|
} `json:"pattern"`
|
||||||
|
} `json:"chartarea"`
|
||||||
|
Plotarea struct {
|
||||||
|
ShowBubbleSize bool `json:"show_bubble_size"`
|
||||||
|
ShowCatName bool `json:"show_cat_name"`
|
||||||
|
ShowLeaderLines bool `json:"show_leader_lines"`
|
||||||
|
ShowPercent bool `json:"show_percent"`
|
||||||
|
ShowSerName bool `json:"show_series_name"`
|
||||||
|
ShowVal bool `json:"show_val"`
|
||||||
|
Gradient struct {
|
||||||
|
Colors []string `json:"colors"`
|
||||||
|
} `json:"gradient"`
|
||||||
|
Border struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
DashType string `json:"dash_type"`
|
||||||
|
} `json:"border"`
|
||||||
|
Fill struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
} `json:"fill"`
|
||||||
|
Layout formatLayout `json:"layout"`
|
||||||
|
} `json:"plotarea"`
|
||||||
|
ShowBlanksAs string `json:"show_blanks_as"`
|
||||||
|
ShowHiddenData bool `json:"show_hidden_data"`
|
||||||
|
SetRotation int `json:"set_rotation"`
|
||||||
|
SetHoleSize int `json:"set_hole_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatChartLegend directly maps the format settings of the chart legend.
|
||||||
|
type formatChartLegend struct {
|
||||||
|
None bool `json:"none"`
|
||||||
|
DeleteSeries []int `json:"delete_series"`
|
||||||
|
Font formatFont `json:"font"`
|
||||||
|
Layout formatLayout `json:"layout"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
ShowLegendEntry bool `json:"show_legend_entry"`
|
||||||
|
ShowLegendKey bool `json:"show_legend_key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatChartSeries directly maps the format settings of the chart series.
|
||||||
|
type formatChartSeries struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Categories string `json:"categories"`
|
||||||
|
Values string `json:"values"`
|
||||||
|
Line struct {
|
||||||
|
None bool `json:"none"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
} `json:"line"`
|
||||||
|
Marker struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Width float64 `json:"width"`
|
||||||
|
Border struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
None bool `json:"none"`
|
||||||
|
} `json:"border"`
|
||||||
|
Fill struct {
|
||||||
|
Color string `json:"color"`
|
||||||
|
None bool `json:"none"`
|
||||||
|
} `json:"fill"`
|
||||||
|
} `json:"marker"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatChartTitle directly maps the format settings of the chart title.
|
||||||
|
type formatChartTitle struct {
|
||||||
|
None bool `json:"none"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Overlay bool `json:"overlay"`
|
||||||
|
Layout formatLayout `json:"layout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatLayout directly maps the format settings of the element layout.
|
||||||
|
type formatLayout struct {
|
||||||
|
X float64 `json:"x"`
|
||||||
|
Y float64 `json:"y"`
|
||||||
|
Width float64 `json:"width"`
|
||||||
|
Height float64 `json:"height"`
|
||||||
|
}
|
||||||
72
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlComments.go
generated
vendored
Normal file
72
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlComments.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// xlsxComments directly maps the comments element from the namespace
|
||||||
|
// http://schemas.openxmlformats.org/spreadsheetml/2006/main. A comment is a
|
||||||
|
// rich text note that is attached to and associated with a cell, separate from
|
||||||
|
// other cell content. Comment content is stored separate from the cell, and is
|
||||||
|
// displayed in a drawing object (like a text box) that is separate from, but
|
||||||
|
// associated with, a cell. Comments are used as reminders, such as noting how a
|
||||||
|
// complex formula works, or to provide feedback to other users. Comments can
|
||||||
|
// also be used to explain assumptions made in a formula or to call out
|
||||||
|
// something special about the cell.
|
||||||
|
type xlsxComments struct {
|
||||||
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main comments"`
|
||||||
|
Authors []xlsxAuthor `xml:"authors"`
|
||||||
|
CommentList xlsxCommentList `xml:"commentList"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxAuthor directly maps the author element. This element holds a string
|
||||||
|
// representing the name of a single author of comments. Every comment shall
|
||||||
|
// have an author. The maximum length of the author string is an implementation
|
||||||
|
// detail, but a good guideline is 255 chars.
|
||||||
|
type xlsxAuthor struct {
|
||||||
|
Author string `xml:"author"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxCommentList (List of Comments) directly maps the xlsxCommentList element.
|
||||||
|
// This element is a container that holds a list of comments for the sheet.
|
||||||
|
type xlsxCommentList struct {
|
||||||
|
Comment []xlsxComment `xml:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxComment directly maps the comment element. This element represents a
|
||||||
|
// single user entered comment. Each comment shall have an author and can
|
||||||
|
// optionally contain richly formatted text.
|
||||||
|
type xlsxComment struct {
|
||||||
|
Ref string `xml:"ref,attr"`
|
||||||
|
AuthorID int `xml:"authorId,attr"`
|
||||||
|
Text xlsxText `xml:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxText directly maps the text element. This element contains rich text
|
||||||
|
// which represents the text of a comment. The maximum length for this text is a
|
||||||
|
// spreadsheet application implementation detail. A recommended guideline is
|
||||||
|
// 32767 chars.
|
||||||
|
type xlsxText struct {
|
||||||
|
R []xlsxR `xml:"r"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatComment directly maps the format settings of the comment.
|
||||||
|
type formatComment struct {
|
||||||
|
Author string `json:"author"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment directly maps the comment information.
|
||||||
|
type Comment struct {
|
||||||
|
Author string `json:"author"`
|
||||||
|
AuthorID int `json:"author_id"`
|
||||||
|
Ref string `json:"ref"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
35
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlContentTypes.go
generated
vendored
Normal file
35
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlContentTypes.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// xlsxTypes directly maps the types element of content types for relationship
|
||||||
|
// parts, it takes a Multipurpose Internet Mail Extension (MIME) media type as a
|
||||||
|
// value.
|
||||||
|
type xlsxTypes struct {
|
||||||
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/content-types Types"`
|
||||||
|
Overrides []xlsxOverride `xml:"Override"`
|
||||||
|
Defaults []xlsxDefault `xml:"Default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxOverride directly maps the override element in the namespace
|
||||||
|
// http://schemas.openxmlformats.org/package/2006/content-types
|
||||||
|
type xlsxOverride struct {
|
||||||
|
PartName string `xml:",attr"`
|
||||||
|
ContentType string `xml:",attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxDefault directly maps the default element in the namespace
|
||||||
|
// http://schemas.openxmlformats.org/package/2006/content-types
|
||||||
|
type xlsxDefault struct {
|
||||||
|
Extension string `xml:",attr"`
|
||||||
|
ContentType string `xml:",attr"`
|
||||||
|
}
|
||||||
196
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlDecodeDrawing.go
generated
vendored
Normal file
196
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlDecodeDrawing.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// decodeCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape Size)
|
||||||
|
// and twoCellAnchor (Two Cell Anchor Shape Size). This element specifies a two
|
||||||
|
// cell anchor placeholder for a group, a shape, or a drawing element. It moves
|
||||||
|
// with cells and its extents are in EMU units.
|
||||||
|
type decodeCellAnchor struct {
|
||||||
|
EditAs string `xml:"editAs,attr,omitempty"`
|
||||||
|
Content string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeWsDr directly maps the root element for a part of this content type
|
||||||
|
// shall wsDr. In order to solve the problem that the label structure is changed
|
||||||
|
// after serialization and deserialization, two different structures are
|
||||||
|
// defined. decodeWsDr just for deserialization.
|
||||||
|
type decodeWsDr struct {
|
||||||
|
A string `xml:"xmlns a,attr"`
|
||||||
|
Xdr string `xml:"xmlns xdr,attr"`
|
||||||
|
R string `xml:"xmlns r,attr"`
|
||||||
|
OneCellAnchor []*decodeCellAnchor `xml:"oneCellAnchor,omitempty"`
|
||||||
|
TwoCellAnchor []*decodeCellAnchor `xml:"twoCellAnchor,omitempty"`
|
||||||
|
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing wsDr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeTwoCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape
|
||||||
|
// Size) and twoCellAnchor (Two Cell Anchor Shape Size). This element specifies
|
||||||
|
// a two cell anchor placeholder for a group, a shape, or a drawing element. It
|
||||||
|
// moves with cells and its extents are in EMU units.
|
||||||
|
type decodeTwoCellAnchor struct {
|
||||||
|
From *decodeFrom `xml:"from"`
|
||||||
|
To *decodeTo `xml:"to"`
|
||||||
|
Pic *decodePic `xml:"pic,omitempty"`
|
||||||
|
ClientData *decodeClientData `xml:"clientData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
|
||||||
|
// element specifies non-visual canvas properties. This allows for additional
|
||||||
|
// information that does not affect the appearance of the picture to be stored.
|
||||||
|
type decodeCNvPr struct {
|
||||||
|
ID int `xml:"id,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Descr string `xml:"descr,attr"`
|
||||||
|
Title string `xml:"title,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePicLocks directly maps the picLocks (Picture Locks). This element
|
||||||
|
// specifies all locking properties for a graphic frame. These properties inform
|
||||||
|
// the generating application about specific properties that have been
|
||||||
|
// previously locked and thus should not be changed.
|
||||||
|
type decodePicLocks struct {
|
||||||
|
NoAdjustHandles bool `xml:"noAdjustHandles,attr,omitempty"`
|
||||||
|
NoChangeArrowheads bool `xml:"noChangeArrowheads,attr,omitempty"`
|
||||||
|
NoChangeAspect bool `xml:"noChangeAspect,attr"`
|
||||||
|
NoChangeShapeType bool `xml:"noChangeShapeType,attr,omitempty"`
|
||||||
|
NoCrop bool `xml:"noCrop,attr,omitempty"`
|
||||||
|
NoEditPoints bool `xml:"noEditPoints,attr,omitempty"`
|
||||||
|
NoGrp bool `xml:"noGrp,attr,omitempty"`
|
||||||
|
NoMove bool `xml:"noMove,attr,omitempty"`
|
||||||
|
NoResize bool `xml:"noResize,attr,omitempty"`
|
||||||
|
NoRot bool `xml:"noRot,attr,omitempty"`
|
||||||
|
NoSelect bool `xml:"noSelect,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeBlip directly maps the blip element in the namespace
|
||||||
|
// http://purl.oclc.org/ooxml/officeDoc ument/relationships - This element
|
||||||
|
// specifies the existence of an image (binary large image or picture) and
|
||||||
|
// contains a reference to the image data.
|
||||||
|
type decodeBlip struct {
|
||||||
|
Embed string `xml:"embed,attr"`
|
||||||
|
Cstate string `xml:"cstate,attr,omitempty"`
|
||||||
|
R string `xml:"r,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeStretch directly maps the stretch element. This element specifies that
|
||||||
|
// a BLIP should be stretched to fill the target rectangle. The other option is
|
||||||
|
// a tile where a BLIP is tiled to fill the available area.
|
||||||
|
type decodeStretch struct {
|
||||||
|
FillRect string `xml:"fillRect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeOff directly maps the colOff and rowOff element. This element is used
|
||||||
|
// to specify the column offset within a cell.
|
||||||
|
type decodeOff struct {
|
||||||
|
X int `xml:"x,attr"`
|
||||||
|
Y int `xml:"y,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeExt directly maps the ext element.
|
||||||
|
type decodeExt struct {
|
||||||
|
Cx int `xml:"cx,attr"`
|
||||||
|
Cy int `xml:"cy,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePrstGeom directly maps the prstGeom (Preset geometry). This element
|
||||||
|
// specifies when a preset geometric shape should be used instead of a custom
|
||||||
|
// geometric shape. The generating application should be able to render all
|
||||||
|
// preset geometries enumerated in the ST_ShapeType list.
|
||||||
|
type decodePrstGeom struct {
|
||||||
|
Prst string `xml:"prst,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeXfrm directly maps the xfrm (2D Transform for Graphic Frame). This
|
||||||
|
// element specifies the transform to be applied to the corresponding graphic
|
||||||
|
// frame. This transformation is applied to the graphic frame just as it would
|
||||||
|
// be for a shape or group shape.
|
||||||
|
type decodeXfrm struct {
|
||||||
|
Off decodeOff `xml:"off"`
|
||||||
|
Ext decodeExt `xml:"ext"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing
|
||||||
|
// Properties). This element specifies the non-visual properties for the picture
|
||||||
|
// canvas. These properties are to be used by the generating application to
|
||||||
|
// determine how certain properties are to be changed for the picture object in
|
||||||
|
// question.
|
||||||
|
type decodeCNvPicPr struct {
|
||||||
|
PicLocks decodePicLocks `xml:"picLocks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// directly maps the nvPicPr (Non-Visual Properties for a Picture). This element
|
||||||
|
// specifies all non-visual properties for a picture. This element is a
|
||||||
|
// container for the non-visual identification properties, shape properties and
|
||||||
|
// application properties that are to be associated with a picture. This allows
|
||||||
|
// for additional information that does not affect the appearance of the picture
|
||||||
|
// to be stored.
|
||||||
|
type decodeNvPicPr struct {
|
||||||
|
CNvPr decodeCNvPr `xml:"cNvPr"`
|
||||||
|
CNvPicPr decodeCNvPicPr `xml:"cNvPicPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeBlipFill directly maps the blipFill (Picture Fill). This element
|
||||||
|
// specifies the kind of picture fill that the picture object has. Because a
|
||||||
|
// picture has a picture fill already by default, it is possible to have two
|
||||||
|
// fills specified for a picture object.
|
||||||
|
type decodeBlipFill struct {
|
||||||
|
Blip decodeBlip `xml:"blip"`
|
||||||
|
Stretch decodeStretch `xml:"stretch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeSpPr directly maps the spPr (Shape Properties). This element specifies
|
||||||
|
// the visual shape properties that can be applied to a picture. These are the
|
||||||
|
// same properties that are allowed to describe the visual properties of a shape
|
||||||
|
// but are used here to describe the visual appearance of a picture within a
|
||||||
|
// document.
|
||||||
|
type decodeSpPr struct {
|
||||||
|
Xfrm decodeXfrm `xml:"a:xfrm"`
|
||||||
|
PrstGeom decodePrstGeom `xml:"a:prstGeom"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePic elements encompass the definition of pictures within the DrawingML
|
||||||
|
// framework. While pictures are in many ways very similar to shapes they have
|
||||||
|
// specific properties that are unique in order to optimize for picture-
|
||||||
|
// specific scenarios.
|
||||||
|
type decodePic struct {
|
||||||
|
NvPicPr decodeNvPicPr `xml:"nvPicPr"`
|
||||||
|
BlipFill decodeBlipFill `xml:"blipFill"`
|
||||||
|
SpPr decodeSpPr `xml:"spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeFrom specifies the starting anchor.
|
||||||
|
type decodeFrom struct {
|
||||||
|
Col int `xml:"col"`
|
||||||
|
ColOff int `xml:"colOff"`
|
||||||
|
Row int `xml:"row"`
|
||||||
|
RowOff int `xml:"rowOff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeTo directly specifies the ending anchor.
|
||||||
|
type decodeTo struct {
|
||||||
|
Col int `xml:"col"`
|
||||||
|
ColOff int `xml:"colOff"`
|
||||||
|
Row int `xml:"row"`
|
||||||
|
RowOff int `xml:"rowOff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeClientData directly maps the clientData element. An empty element which
|
||||||
|
// specifies (via attributes) certain properties related to printing and
|
||||||
|
// selection of the drawing object. The fLocksWithSheet attribute (either true
|
||||||
|
// or false) determines whether to disable selection when the sheet is
|
||||||
|
// protected, and fPrintsWithSheet attribute (either true or false) determines
|
||||||
|
// whether the object is printed when the sheet is printed.
|
||||||
|
type decodeClientData struct {
|
||||||
|
FLocksWithSheet bool `xml:"fLocksWithSheet,attr"`
|
||||||
|
FPrintsWithSheet bool `xml:"fPrintsWithSheet,attr"`
|
||||||
|
}
|
||||||
402
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlDrawing.go
generated
vendored
Normal file
402
vendor/github.com/360EntSecGroup-Skylar/excelize/xmlDrawing.go
generated
vendored
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
// Copyright 2016 - 2019 The excelize Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in
|
||||||
|
// the LICENSE file.
|
||||||
|
//
|
||||||
|
// Package excelize providing a set of functions that allow you to write to
|
||||||
|
// and read from XLSX files. Support reads and writes XLSX file generated by
|
||||||
|
// Microsoft Excel™ 2007 and later. Support save file without losing original
|
||||||
|
// charts of XLSX. This library needs Go version 1.8 or later.
|
||||||
|
|
||||||
|
package excelize
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
// Source relationship and namespace.
|
||||||
|
const (
|
||||||
|
SourceRelationship = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||||
|
SourceRelationshipChart = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
|
||||||
|
SourceRelationshipComments = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
|
||||||
|
SourceRelationshipImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
|
||||||
|
SourceRelationshipTable = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table"
|
||||||
|
SourceRelationshipDrawingML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"
|
||||||
|
SourceRelationshipDrawingVML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing"
|
||||||
|
SourceRelationshipHyperLink = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
||||||
|
SourceRelationshipWorkSheet = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"
|
||||||
|
SourceRelationshipChart201506 = "http://schemas.microsoft.com/office/drawing/2015/06/chart"
|
||||||
|
SourceRelationshipChart20070802 = "http://schemas.microsoft.com/office/drawing/2007/8/2/chart"
|
||||||
|
SourceRelationshipChart2014 = "http://schemas.microsoft.com/office/drawing/2014/chart"
|
||||||
|
SourceRelationshipCompatibility = "http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
NameSpaceDrawingML = "http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
NameSpaceDrawingMLChart = "http://schemas.openxmlformats.org/drawingml/2006/chart"
|
||||||
|
NameSpaceDrawingMLSpreadSheet = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
|
||||||
|
NameSpaceSpreadSheet = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||||
|
NameSpaceXML = "http://www.w3.org/XML/1998/namespace"
|
||||||
|
StrictSourceRelationship = "http://purl.oclc.org/ooxml/officeDocument/relationships"
|
||||||
|
StrictSourceRelationshipChart = "http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
|
||||||
|
StrictSourceRelationshipComments = "http://purl.oclc.org/ooxml/officeDocument/relationships/comments"
|
||||||
|
StrictSourceRelationshipImage = "http://purl.oclc.org/ooxml/officeDocument/relationships/image"
|
||||||
|
StrictNameSpaceSpreadSheet = "http://purl.oclc.org/ooxml/spreadsheetml/main"
|
||||||
|
)
|
||||||
|
|
||||||
|
var supportImageTypes = map[string]string{".gif": ".gif", ".jpg": ".jpeg", ".jpeg": ".jpeg", ".png": ".png"}
|
||||||
|
|
||||||
|
// xlsxCNvPr directly maps the cNvPr (Non-Visual Drawing Properties). This
|
||||||
|
// element specifies non-visual canvas properties. This allows for additional
|
||||||
|
// information that does not affect the appearance of the picture to be stored.
|
||||||
|
type xlsxCNvPr struct {
|
||||||
|
ID int `xml:"id,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Descr string `xml:"descr,attr"`
|
||||||
|
Title string `xml:"title,attr,omitempty"`
|
||||||
|
HlinkClick *xlsxHlinkClick `xml:"a:hlinkClick"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxHlinkClick (Click Hyperlink) Specifies the on-click hyperlink
|
||||||
|
// information to be applied to a run of text. When the hyperlink text is
|
||||||
|
// clicked the link is fetched.
|
||||||
|
type xlsxHlinkClick struct {
|
||||||
|
R string `xml:"xmlns:r,attr,omitempty"`
|
||||||
|
RID string `xml:"r:id,attr,omitempty"`
|
||||||
|
InvalidURL string `xml:"invalidUrl,attr,omitempty"`
|
||||||
|
Action string `xml:"action,attr,omitempty"`
|
||||||
|
TgtFrame string `xml:"tgtFrame,attr,omitempty"`
|
||||||
|
Tooltip string `xml:"tooltip,attr,omitempty"`
|
||||||
|
History bool `xml:"history,attr,omitempty"`
|
||||||
|
HighlightClick bool `xml:"highlightClick,attr,omitempty"`
|
||||||
|
EndSnd bool `xml:"endSnd,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxPicLocks directly maps the picLocks (Picture Locks). This element
|
||||||
|
// specifies all locking properties for a graphic frame. These properties inform
|
||||||
|
// the generating application about specific properties that have been
|
||||||
|
// previously locked and thus should not be changed.
|
||||||
|
type xlsxPicLocks struct {
|
||||||
|
NoAdjustHandles bool `xml:"noAdjustHandles,attr,omitempty"`
|
||||||
|
NoChangeArrowheads bool `xml:"noChangeArrowheads,attr,omitempty"`
|
||||||
|
NoChangeAspect bool `xml:"noChangeAspect,attr"`
|
||||||
|
NoChangeShapeType bool `xml:"noChangeShapeType,attr,omitempty"`
|
||||||
|
NoCrop bool `xml:"noCrop,attr,omitempty"`
|
||||||
|
NoEditPoints bool `xml:"noEditPoints,attr,omitempty"`
|
||||||
|
NoGrp bool `xml:"noGrp,attr,omitempty"`
|
||||||
|
NoMove bool `xml:"noMove,attr,omitempty"`
|
||||||
|
NoResize bool `xml:"noResize,attr,omitempty"`
|
||||||
|
NoRot bool `xml:"noRot,attr,omitempty"`
|
||||||
|
NoSelect bool `xml:"noSelect,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxBlip directly maps the blip element in the namespace
|
||||||
|
// http://purl.oclc.org/ooxml/officeDoc ument/relationships - This element
|
||||||
|
// specifies the existence of an image (binary large image or picture) and
|
||||||
|
// contains a reference to the image data.
|
||||||
|
type xlsxBlip struct {
|
||||||
|
Embed string `xml:"r:embed,attr"`
|
||||||
|
Cstate string `xml:"cstate,attr,omitempty"`
|
||||||
|
R string `xml:"xmlns:r,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxStretch directly maps the stretch element. This element specifies that a
|
||||||
|
// BLIP should be stretched to fill the target rectangle. The other option is a
|
||||||
|
// tile where a BLIP is tiled to fill the available area.
|
||||||
|
type xlsxStretch struct {
|
||||||
|
FillRect string `xml:"a:fillRect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxOff directly maps the colOff and rowOff element. This element is used to
|
||||||
|
// specify the column offset within a cell.
|
||||||
|
type xlsxOff struct {
|
||||||
|
X int `xml:"x,attr"`
|
||||||
|
Y int `xml:"y,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxExt directly maps the ext element.
|
||||||
|
type xlsxExt struct {
|
||||||
|
Cx int `xml:"cx,attr"`
|
||||||
|
Cy int `xml:"cy,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxPrstGeom directly maps the prstGeom (Preset geometry). This element
|
||||||
|
// specifies when a preset geometric shape should be used instead of a custom
|
||||||
|
// geometric shape. The generating application should be able to render all
|
||||||
|
// preset geometries enumerated in the ST_ShapeType list.
|
||||||
|
type xlsxPrstGeom struct {
|
||||||
|
Prst string `xml:"prst,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxXfrm directly maps the xfrm (2D Transform for Graphic Frame). This
|
||||||
|
// element specifies the transform to be applied to the corresponding graphic
|
||||||
|
// frame. This transformation is applied to the graphic frame just as it would
|
||||||
|
// be for a shape or group shape.
|
||||||
|
type xlsxXfrm struct {
|
||||||
|
Off xlsxOff `xml:"a:off"`
|
||||||
|
Ext xlsxExt `xml:"a:ext"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxCNvPicPr directly maps the cNvPicPr (Non-Visual Picture Drawing
|
||||||
|
// Properties). This element specifies the non-visual properties for the picture
|
||||||
|
// canvas. These properties are to be used by the generating application to
|
||||||
|
// determine how certain properties are to be changed for the picture object in
|
||||||
|
// question.
|
||||||
|
type xlsxCNvPicPr struct {
|
||||||
|
PicLocks xlsxPicLocks `xml:"a:picLocks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// directly maps the nvPicPr (Non-Visual Properties for a Picture). This element
|
||||||
|
// specifies all non-visual properties for a picture. This element is a
|
||||||
|
// container for the non-visual identification properties, shape properties and
|
||||||
|
// application properties that are to be associated with a picture. This allows
|
||||||
|
// for additional information that does not affect the appearance of the picture
|
||||||
|
// to be stored.
|
||||||
|
type xlsxNvPicPr struct {
|
||||||
|
CNvPr xlsxCNvPr `xml:"xdr:cNvPr"`
|
||||||
|
CNvPicPr xlsxCNvPicPr `xml:"xdr:cNvPicPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxBlipFill directly maps the blipFill (Picture Fill). This element
|
||||||
|
// specifies the kind of picture fill that the picture object has. Because a
|
||||||
|
// picture has a picture fill already by default, it is possible to have two
|
||||||
|
// fills specified for a picture object.
|
||||||
|
type xlsxBlipFill struct {
|
||||||
|
Blip xlsxBlip `xml:"a:blip"`
|
||||||
|
Stretch xlsxStretch `xml:"a:stretch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxSpPr directly maps the spPr (Shape Properties). This element specifies
|
||||||
|
// the visual shape properties that can be applied to a picture. These are the
|
||||||
|
// same properties that are allowed to describe the visual properties of a shape
|
||||||
|
// but are used here to describe the visual appearance of a picture within a
|
||||||
|
// document.
|
||||||
|
type xlsxSpPr struct {
|
||||||
|
Xfrm xlsxXfrm `xml:"a:xfrm"`
|
||||||
|
PrstGeom xlsxPrstGeom `xml:"a:prstGeom"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxPic elements encompass the definition of pictures within the DrawingML
|
||||||
|
// framework. While pictures are in many ways very similar to shapes they have
|
||||||
|
// specific properties that are unique in order to optimize for picture-
|
||||||
|
// specific scenarios.
|
||||||
|
type xlsxPic struct {
|
||||||
|
NvPicPr xlsxNvPicPr `xml:"xdr:nvPicPr"`
|
||||||
|
BlipFill xlsxBlipFill `xml:"xdr:blipFill"`
|
||||||
|
SpPr xlsxSpPr `xml:"xdr:spPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxFrom specifies the starting anchor.
|
||||||
|
type xlsxFrom struct {
|
||||||
|
Col int `xml:"xdr:col"`
|
||||||
|
ColOff int `xml:"xdr:colOff"`
|
||||||
|
Row int `xml:"xdr:row"`
|
||||||
|
RowOff int `xml:"xdr:rowOff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxTo directly specifies the ending anchor.
|
||||||
|
type xlsxTo struct {
|
||||||
|
Col int `xml:"xdr:col"`
|
||||||
|
ColOff int `xml:"xdr:colOff"`
|
||||||
|
Row int `xml:"xdr:row"`
|
||||||
|
RowOff int `xml:"xdr:rowOff"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrClientData directly maps the clientData element. An empty element which
|
||||||
|
// specifies (via attributes) certain properties related to printing and
|
||||||
|
// selection of the drawing object. The fLocksWithSheet attribute (either true
|
||||||
|
// or false) determines whether to disable selection when the sheet is
|
||||||
|
// protected, and fPrintsWithSheet attribute (either true or false) determines
|
||||||
|
// whether the object is printed when the sheet is printed.
|
||||||
|
type xdrClientData struct {
|
||||||
|
FLocksWithSheet bool `xml:"fLocksWithSheet,attr"`
|
||||||
|
FPrintsWithSheet bool `xml:"fPrintsWithSheet,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrCellAnchor directly maps the oneCellAnchor (One Cell Anchor Shape Size)
|
||||||
|
// and twoCellAnchor (Two Cell Anchor Shape Size). This element specifies a two
|
||||||
|
// cell anchor placeholder for a group, a shape, or a drawing element. It moves
|
||||||
|
// with cells and its extents are in EMU units.
|
||||||
|
type xdrCellAnchor struct {
|
||||||
|
EditAs string `xml:"editAs,attr,omitempty"`
|
||||||
|
From *xlsxFrom `xml:"xdr:from"`
|
||||||
|
To *xlsxTo `xml:"xdr:to"`
|
||||||
|
Ext *xlsxExt `xml:"xdr:ext"`
|
||||||
|
Sp *xdrSp `xml:"xdr:sp"`
|
||||||
|
Pic *xlsxPic `xml:"xdr:pic,omitempty"`
|
||||||
|
GraphicFrame string `xml:",innerxml"`
|
||||||
|
ClientData *xdrClientData `xml:"xdr:clientData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxWsDr directly maps the root element for a part of this content type shall
|
||||||
|
// wsDr.
|
||||||
|
type xlsxWsDr struct {
|
||||||
|
XMLName xml.Name `xml:"xdr:wsDr"`
|
||||||
|
OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"`
|
||||||
|
TwoCellAnchor []*xdrCellAnchor `xml:"xdr:twoCellAnchor"`
|
||||||
|
A string `xml:"xmlns:a,attr,omitempty"`
|
||||||
|
Xdr string `xml:"xmlns:xdr,attr,omitempty"`
|
||||||
|
R string `xml:"xmlns:r,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxGraphicFrame (Graphic Frame) directly maps the xdr:graphicFrame element.
|
||||||
|
// This element specifies the existence of a graphics frame. This frame contains
|
||||||
|
// a graphic that was generated by an external source and needs a container in
|
||||||
|
// which to be displayed on the slide surface.
|
||||||
|
type xlsxGraphicFrame struct {
|
||||||
|
XMLName xml.Name `xml:"xdr:graphicFrame"`
|
||||||
|
Macro string `xml:"macro,attr"`
|
||||||
|
NvGraphicFramePr xlsxNvGraphicFramePr `xml:"xdr:nvGraphicFramePr"`
|
||||||
|
Xfrm xlsxXfrm `xml:"xdr:xfrm"`
|
||||||
|
Graphic *xlsxGraphic `xml:"a:graphic"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxNvGraphicFramePr (Non-Visual Properties for a Graphic Frame) directly
|
||||||
|
// maps the xdr:nvGraphicFramePr element. This element specifies all non-visual
|
||||||
|
// properties for a graphic frame. This element is a container for the non-
|
||||||
|
// visual identification properties, shape properties and application properties
|
||||||
|
// that are to be associated with a graphic frame. This allows for additional
|
||||||
|
// information that does not affect the appearance of the graphic frame to be
|
||||||
|
// stored.
|
||||||
|
type xlsxNvGraphicFramePr struct {
|
||||||
|
CNvPr *xlsxCNvPr `xml:"xdr:cNvPr"`
|
||||||
|
ChicNvGraphicFramePr string `xml:"xdr:cNvGraphicFramePr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxGraphic (Graphic Object) directly maps the a:graphic element. This
|
||||||
|
// element specifies the existence of a single graphic object. Document authors
|
||||||
|
// should refer to this element when they wish to persist a graphical object of
|
||||||
|
// some kind. The specification for this graphical object is provided entirely
|
||||||
|
// by the document author and referenced within the graphicData child element.
|
||||||
|
type xlsxGraphic struct {
|
||||||
|
GraphicData *xlsxGraphicData `xml:"a:graphicData"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxGraphicData (Graphic Object Data) directly maps the a:graphicData
|
||||||
|
// element. This element specifies the reference to a graphic object within the
|
||||||
|
// document. This graphic object is provided entirely by the document authors
|
||||||
|
// who choose to persist this data within the document.
|
||||||
|
type xlsxGraphicData struct {
|
||||||
|
URI string `xml:"uri,attr"`
|
||||||
|
Chart *xlsxChart `xml:"c:chart,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlsxChart (Chart) directly maps the c:chart element.
|
||||||
|
type xlsxChart struct {
|
||||||
|
C string `xml:"xmlns:c,attr"`
|
||||||
|
RID string `xml:"r:id,attr"`
|
||||||
|
R string `xml:"xmlns:r,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrSp (Shape) directly maps the xdr:sp element. This element specifies the
|
||||||
|
// existence of a single shape. A shape can either be a preset or a custom
|
||||||
|
// geometry, defined using the SpreadsheetDrawingML framework. In addition to a
|
||||||
|
// geometry each shape can have both visual and non-visual properties attached.
|
||||||
|
// Text and corresponding styling information can also be attached to a shape.
|
||||||
|
// This shape is specified along with all other shapes within either the shape
|
||||||
|
// tree or group shape elements.
|
||||||
|
type xdrSp struct {
|
||||||
|
Macro string `xml:"macro,attr"`
|
||||||
|
Textlink string `xml:"textlink,attr"`
|
||||||
|
NvSpPr *xdrNvSpPr `xml:"xdr:nvSpPr"`
|
||||||
|
SpPr *xlsxSpPr `xml:"xdr:spPr"`
|
||||||
|
Style *xdrStyle `xml:"xdr:style"`
|
||||||
|
TxBody *xdrTxBody `xml:"xdr:txBody"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrNvSpPr (Non-Visual Properties for a Shape) directly maps the xdr:nvSpPr
|
||||||
|
// element. This element specifies all non-visual properties for a shape. This
|
||||||
|
// element is a container for the non-visual identification properties, shape
|
||||||
|
// properties and application properties that are to be associated with a shape.
|
||||||
|
// This allows for additional information that does not affect the appearance of
|
||||||
|
// the shape to be stored.
|
||||||
|
type xdrNvSpPr struct {
|
||||||
|
CNvPr *xlsxCNvPr `xml:"xdr:cNvPr"`
|
||||||
|
CNvSpPr *xdrCNvSpPr `xml:"xdr:cNvSpPr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrCNvSpPr (Connection Non-Visual Shape Properties) directly maps the
|
||||||
|
// xdr:cNvSpPr element. This element specifies the set of non-visual properties
|
||||||
|
// for a connection shape. These properties specify all data about the
|
||||||
|
// connection shape which do not affect its display within a spreadsheet.
|
||||||
|
type xdrCNvSpPr struct {
|
||||||
|
TxBox bool `xml:"txBox,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrStyle (Shape Style) directly maps the xdr:style element. The element
|
||||||
|
// specifies the style that is applied to a shape and the corresponding
|
||||||
|
// references for each of the style components such as lines and fills.
|
||||||
|
type xdrStyle struct {
|
||||||
|
LnRef *aRef `xml:"a:lnRef"`
|
||||||
|
FillRef *aRef `xml:"a:fillRef"`
|
||||||
|
EffectRef *aRef `xml:"a:effectRef"`
|
||||||
|
FontRef *aFontRef `xml:"a:fontRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aRef directly maps the a:lnRef, a:fillRef and a:effectRef element.
|
||||||
|
type aRef struct {
|
||||||
|
Idx int `xml:"idx,attr"`
|
||||||
|
ScrgbClr *aScrgbClr `xml:"a:scrgbClr"`
|
||||||
|
SchemeClr *attrValString `xml:"a:schemeClr"`
|
||||||
|
SrgbClr *attrValString `xml:"a:srgbClr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aScrgbClr (RGB Color Model - Percentage Variant) directly maps the a:scrgbClr
|
||||||
|
// element. This element specifies a color using the red, green, blue RGB color
|
||||||
|
// model. Each component, red, green, and blue is expressed as a percentage from
|
||||||
|
// 0% to 100%. A linear gamma of 1.0 is assumed.
|
||||||
|
type aScrgbClr struct {
|
||||||
|
R float64 `xml:"r,attr"`
|
||||||
|
G float64 `xml:"g,attr"`
|
||||||
|
B float64 `xml:"b,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// aFontRef (Font Reference) directly maps the a:fontRef element. This element
|
||||||
|
// represents a reference to a themed font. When used it specifies which themed
|
||||||
|
// font to use along with a choice of color.
|
||||||
|
type aFontRef struct {
|
||||||
|
Idx string `xml:"idx,attr"`
|
||||||
|
SchemeClr *attrValString `xml:"a:schemeClr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrTxBody (Shape Text Body) directly maps the xdr:txBody element. This
|
||||||
|
// element specifies the existence of text to be contained within the
|
||||||
|
// corresponding shape. All visible text and visible text related properties are
|
||||||
|
// contained within this element. There can be multiple paragraphs and within
|
||||||
|
// paragraphs multiple runs of text.
|
||||||
|
type xdrTxBody struct {
|
||||||
|
BodyPr *aBodyPr `xml:"a:bodyPr"`
|
||||||
|
P []*aP `xml:"a:p"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatPicture directly maps the format settings of the picture.
|
||||||
|
type formatPicture struct {
|
||||||
|
FPrintsWithSheet bool `json:"print_obj"`
|
||||||
|
FLocksWithSheet bool `json:"locked"`
|
||||||
|
NoChangeAspect bool `json:"lock_aspect_ratio"`
|
||||||
|
OffsetX int `json:"x_offset"`
|
||||||
|
OffsetY int `json:"y_offset"`
|
||||||
|
XScale float64 `json:"x_scale"`
|
||||||
|
YScale float64 `json:"y_scale"`
|
||||||
|
Hyperlink string `json:"hyperlink"`
|
||||||
|
HyperlinkType string `json:"hyperlink_type"`
|
||||||
|
Positioning string `json:"positioning"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatShape directly maps the format settings of the shape.
|
||||||
|
type formatShape struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
Format formatPicture `json:"format"`
|
||||||
|
Color formatShapeColor `json:"color"`
|
||||||
|
Paragraph []formatShapeParagraph `json:"paragraph"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatShapeParagraph directly maps the format settings of the paragraph in
|
||||||
|
// the shape.
|
||||||
|
type formatShapeParagraph struct {
|
||||||
|
Font formatFont `json:"font"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatShapeColor directly maps the color settings of the shape.
|
||||||
|
type formatShapeColor struct {
|
||||||
|
Line string `json:"line"`
|
||||||
|
Fill string `json:"fill"`
|
||||||
|
Effect string `json:"effect"`
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user